@genesislcap/ai-assistant 14.444.0 → 14.444.1
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.
- package/dist/ai-assistant.api.json +80 -2
- package/dist/ai-assistant.d.ts +59 -16
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +36 -6
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +23 -10
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/state/ai-assistant-slice.d.ts +13 -1
- package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
- package/dist/dts/state/session-store.d.ts +1 -0
- package/dist/dts/state/session-store.d.ts.map +1 -1
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +69 -29
- package/dist/esm/main/main.js +62 -24
- package/dist/esm/state/ai-assistant-slice.js +4 -0
- package/package.json +16 -16
- package/src/components/orchestrating-driver/orchestrating-driver.ts +70 -29
- package/src/main/main.ts +60 -20
- package/src/state/ai-assistant-slice.ts +16 -1
|
@@ -4805,6 +4805,36 @@
|
|
|
4805
4805
|
"isProtected": false,
|
|
4806
4806
|
"isAbstract": false
|
|
4807
4807
|
},
|
|
4808
|
+
{
|
|
4809
|
+
"kind": "Property",
|
|
4810
|
+
"canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#flowOwnerAgentName:member",
|
|
4811
|
+
"docComment": "/**\n * Name of the agent that currently owns a guided flow. Set by the orchestrator when a stateful agent's `onActivate` fires, cleared when the agent calls `releaseAgent` from a terminal-state tool handler. While non-null, routing precedence goes to this agent and the picker is locked.\n *\n * Independent of `pinnedAgentName`: a flow can complete and release without disturbing the user's sticky picker choice.\n */\n",
|
|
4812
|
+
"excerptTokens": [
|
|
4813
|
+
{
|
|
4814
|
+
"kind": "Content",
|
|
4815
|
+
"text": "get flowOwnerAgentName(): "
|
|
4816
|
+
},
|
|
4817
|
+
{
|
|
4818
|
+
"kind": "Content",
|
|
4819
|
+
"text": "string | null"
|
|
4820
|
+
},
|
|
4821
|
+
{
|
|
4822
|
+
"kind": "Content",
|
|
4823
|
+
"text": ";\n\nset flowOwnerAgentName(value: string | null);"
|
|
4824
|
+
}
|
|
4825
|
+
],
|
|
4826
|
+
"isReadonly": false,
|
|
4827
|
+
"isOptional": false,
|
|
4828
|
+
"releaseTag": "Beta",
|
|
4829
|
+
"name": "flowOwnerAgentName",
|
|
4830
|
+
"propertyTypeTokenRange": {
|
|
4831
|
+
"startIndex": 1,
|
|
4832
|
+
"endIndex": 2
|
|
4833
|
+
},
|
|
4834
|
+
"isStatic": false,
|
|
4835
|
+
"isProtected": false,
|
|
4836
|
+
"isAbstract": false
|
|
4837
|
+
},
|
|
4808
4838
|
{
|
|
4809
4839
|
"kind": "Method",
|
|
4810
4840
|
"canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#getDebugLog:member(1)",
|
|
@@ -5587,7 +5617,7 @@
|
|
|
5587
5617
|
{
|
|
5588
5618
|
"kind": "Property",
|
|
5589
5619
|
"canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#pinLocked:member",
|
|
5590
|
-
"docComment": "/**\n * The pin is locked when a stateful
|
|
5620
|
+
"docComment": "/**\n * The pin is locked when a stateful flow is mid-stream — i.e. some agent's `onActivate` has fired and it owns live state, and it hasn't yet called `releaseAgent`. Derives directly from `flowOwnerAgentName`, the single source of truth the orchestrator writes on activation and clears on release. Until the user sends their first message, a freshly pinned stateful agent is not yet active and the picker should remain free; the user might change their mind and unpin without anything to clean up.\n */\n",
|
|
5591
5621
|
"excerptTokens": [
|
|
5592
5622
|
{
|
|
5593
5623
|
"kind": "Content",
|
|
@@ -5677,7 +5707,7 @@
|
|
|
5677
5707
|
{
|
|
5678
5708
|
"kind": "Property",
|
|
5679
5709
|
"canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#pinnedAgentName:member",
|
|
5680
|
-
"docComment": "/**\n * Name of the agent the user has pinned via the agent picker. `null` means automatic routing (Auto). Persisted on the session store, so it survives pop-in/pop-out but resets on page refresh.\n */\n",
|
|
5710
|
+
"docComment": "/**\n * Name of the agent the user has pinned via the agent picker. `null` means automatic routing (Auto). Persisted on the session store, so it survives pop-in/pop-out but resets on page refresh.\n *\n * Only changes on explicit user action (picker click or host's `setAgent`). The orchestrator's auto-pin on stateful activation writes `flowOwnerAgentName` instead, so finishing a guided flow doesn't wipe the user's sticky pick.\n */\n",
|
|
5681
5711
|
"excerptTokens": [
|
|
5682
5712
|
{
|
|
5683
5713
|
"kind": "Content",
|
|
@@ -7756,6 +7786,54 @@
|
|
|
7756
7786
|
"isAbstract": false,
|
|
7757
7787
|
"name": "sendMessage"
|
|
7758
7788
|
},
|
|
7789
|
+
{
|
|
7790
|
+
"kind": "Method",
|
|
7791
|
+
"canonicalReference": "@genesislcap/ai-assistant!OrchestratingDriver#setFlowOwner:member(1)",
|
|
7792
|
+
"docComment": "/**\n * Set the flow-owner lock — used to restore state from Redux on driver reconstruction (agents-array changes, pop-in/pop-out). Not intended for external callers to manipulate routing; that's what `setPinnedAgent` is for.\n */\n",
|
|
7793
|
+
"excerptTokens": [
|
|
7794
|
+
{
|
|
7795
|
+
"kind": "Content",
|
|
7796
|
+
"text": "setFlowOwner(name: "
|
|
7797
|
+
},
|
|
7798
|
+
{
|
|
7799
|
+
"kind": "Content",
|
|
7800
|
+
"text": "string | null"
|
|
7801
|
+
},
|
|
7802
|
+
{
|
|
7803
|
+
"kind": "Content",
|
|
7804
|
+
"text": "): "
|
|
7805
|
+
},
|
|
7806
|
+
{
|
|
7807
|
+
"kind": "Content",
|
|
7808
|
+
"text": "void"
|
|
7809
|
+
},
|
|
7810
|
+
{
|
|
7811
|
+
"kind": "Content",
|
|
7812
|
+
"text": ";"
|
|
7813
|
+
}
|
|
7814
|
+
],
|
|
7815
|
+
"isStatic": false,
|
|
7816
|
+
"returnTypeTokenRange": {
|
|
7817
|
+
"startIndex": 3,
|
|
7818
|
+
"endIndex": 4
|
|
7819
|
+
},
|
|
7820
|
+
"releaseTag": "Beta",
|
|
7821
|
+
"isProtected": false,
|
|
7822
|
+
"overloadIndex": 1,
|
|
7823
|
+
"parameters": [
|
|
7824
|
+
{
|
|
7825
|
+
"parameterName": "name",
|
|
7826
|
+
"parameterTypeTokenRange": {
|
|
7827
|
+
"startIndex": 1,
|
|
7828
|
+
"endIndex": 2
|
|
7829
|
+
},
|
|
7830
|
+
"isOptional": false
|
|
7831
|
+
}
|
|
7832
|
+
],
|
|
7833
|
+
"isOptional": false,
|
|
7834
|
+
"isAbstract": false,
|
|
7835
|
+
"name": "setFlowOwner"
|
|
7836
|
+
},
|
|
7759
7837
|
{
|
|
7760
7838
|
"kind": "Method",
|
|
7761
7839
|
"canonicalReference": "@genesislcap/ai-assistant!OrchestratingDriver#setPinnedAgent:member(1)",
|
package/dist/ai-assistant.d.ts
CHANGED
|
@@ -986,9 +986,25 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
986
986
|
* Name of the agent the user has pinned via the agent picker. `null` means
|
|
987
987
|
* automatic routing (Auto). Persisted on the session store, so it survives
|
|
988
988
|
* pop-in/pop-out but resets on page refresh.
|
|
989
|
+
*
|
|
990
|
+
* Only changes on explicit user action (picker click or host's `setAgent`).
|
|
991
|
+
* The orchestrator's auto-pin on stateful activation writes
|
|
992
|
+
* `flowOwnerAgentName` instead, so finishing a guided flow doesn't wipe
|
|
993
|
+
* the user's sticky pick.
|
|
989
994
|
*/
|
|
990
995
|
get pinnedAgentName(): string | null;
|
|
991
996
|
set pinnedAgentName(value: string | null);
|
|
997
|
+
/**
|
|
998
|
+
* Name of the agent that currently owns a guided flow. Set by the
|
|
999
|
+
* orchestrator when a stateful agent's `onActivate` fires, cleared when the
|
|
1000
|
+
* agent calls `releaseAgent` from a terminal-state tool handler. While
|
|
1001
|
+
* non-null, routing precedence goes to this agent and the picker is locked.
|
|
1002
|
+
*
|
|
1003
|
+
* Independent of `pinnedAgentName`: a flow can complete and release
|
|
1004
|
+
* without disturbing the user's sticky picker choice.
|
|
1005
|
+
*/
|
|
1006
|
+
get flowOwnerAgentName(): string | null;
|
|
1007
|
+
set flowOwnerAgentName(value: string | null);
|
|
992
1008
|
get liveSubAgentTrace(): ChatMessage[];
|
|
993
1009
|
set liveSubAgentTrace(value: ChatMessage[]);
|
|
994
1010
|
get liveSubAgentName(): string | null;
|
|
@@ -1160,16 +1176,13 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1160
1176
|
/** Hint text for the currently pinned agent, if any. Used in the toggle button tooltip. */
|
|
1161
1177
|
get pinnedAgentHint(): string | undefined;
|
|
1162
1178
|
/**
|
|
1163
|
-
* The pin is locked when a stateful
|
|
1164
|
-
*
|
|
1165
|
-
*
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
* completes) rather than `pinnedAgentName` (set immediately on picker
|
|
1171
|
-
* click). The serialized `activeAgent` strips lifecycle hooks, so we look
|
|
1172
|
-
* up the live config from `this.agents` to check for them.
|
|
1179
|
+
* The pin is locked when a stateful flow is mid-stream — i.e. some agent's
|
|
1180
|
+
* `onActivate` has fired and it owns live state, and it hasn't yet called
|
|
1181
|
+
* `releaseAgent`. Derives directly from `flowOwnerAgentName`, the
|
|
1182
|
+
* single source of truth the orchestrator writes on activation and clears
|
|
1183
|
+
* on release. Until the user sends their first message, a freshly pinned
|
|
1184
|
+
* stateful agent is not yet active and the picker should remain free; the
|
|
1185
|
+
* user might change their mind and unpin without anything to clean up.
|
|
1173
1186
|
*/
|
|
1174
1187
|
get pinLocked(): boolean;
|
|
1175
1188
|
/** Tooltip shown on the picker toggle button. */
|
|
@@ -1408,7 +1421,21 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
|
|
|
1408
1421
|
* so long-running `onActivate` work can bail if the session disconnects.
|
|
1409
1422
|
*/
|
|
1410
1423
|
private readonly lifecycleAbortController;
|
|
1424
|
+
/**
|
|
1425
|
+
* Sticky user pick from the picker (or the host's `setAgent` API). Only
|
|
1426
|
+
* changes on explicit user action. Survives flow completion: when a stateful
|
|
1427
|
+
* agent releases, the user-pin stays put and routing falls back to it on
|
|
1428
|
+
* the next turn.
|
|
1429
|
+
*/
|
|
1411
1430
|
private pinnedAgentName;
|
|
1431
|
+
/**
|
|
1432
|
+
* Transient flow lock — set when a stateful agent's `onActivate` fires,
|
|
1433
|
+
* cleared by `releaseActiveAgent`. While non-null, routing precedence goes
|
|
1434
|
+
* to this agent and the picker is locked. Independent of
|
|
1435
|
+
* `pinnedAgentName` so completing a flow doesn't wipe the user's sticky
|
|
1436
|
+
* choice.
|
|
1437
|
+
*/
|
|
1438
|
+
private flowOwnerAgentName;
|
|
1412
1439
|
activeAgent?: AgentConfig;
|
|
1413
1440
|
constructor(aiProvider: AIProvider, agents: AgentConfig[], options?: {
|
|
1414
1441
|
sessionKey?: string;
|
|
@@ -1428,6 +1455,13 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
|
|
|
1428
1455
|
* return to automatic routing.
|
|
1429
1456
|
*/
|
|
1430
1457
|
setPinnedAgent(name: string | null): void;
|
|
1458
|
+
/**
|
|
1459
|
+
* Set the flow-owner lock — used to restore state from Redux on driver
|
|
1460
|
+
* reconstruction (agents-array changes, pop-in/pop-out). Not intended for
|
|
1461
|
+
* external callers to manipulate routing; that's what `setPinnedAgent` is
|
|
1462
|
+
* for.
|
|
1463
|
+
*/
|
|
1464
|
+
setFlowOwner(name: string | null): void;
|
|
1431
1465
|
loadHistory(messages: ChatMessage[]): void;
|
|
1432
1466
|
getRawHistory(): readonly ChatMessage[];
|
|
1433
1467
|
/** Delegates to the inner {@link ChatDriver} — turns are captured there. */
|
|
@@ -1437,9 +1471,15 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
|
|
|
1437
1471
|
continueFromHistory(transientPrimer?: ChatMessage[]): Promise<ChatDriverResult>;
|
|
1438
1472
|
private applyAgent;
|
|
1439
1473
|
/**
|
|
1440
|
-
* Release the current stateful agent: fire `onDeactivate`, clear the
|
|
1441
|
-
* dispatch events so the host (and Redux) reflect the
|
|
1442
|
-
* automatically when a tool handler invokes
|
|
1474
|
+
* Release the current stateful agent: fire `onDeactivate`, clear the
|
|
1475
|
+
* flow-owner lock, dispatch events so the host (and Redux) reflect the
|
|
1476
|
+
* unlocked state. Called automatically when a tool handler invokes
|
|
1477
|
+
* `context.releaseAgent`.
|
|
1478
|
+
*
|
|
1479
|
+
* The user-pin (`pinnedAgentName`) is intentionally *not* cleared — a user
|
|
1480
|
+
* who explicitly picked this agent should stay on it after the flow ends,
|
|
1481
|
+
* so the next message starts a fresh flow on the same agent rather than
|
|
1482
|
+
* routing through the classifier.
|
|
1443
1483
|
*/
|
|
1444
1484
|
private releaseActiveAgent;
|
|
1445
1485
|
/**
|
|
@@ -1450,9 +1490,12 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
|
|
|
1450
1490
|
dispose(): Promise<void>;
|
|
1451
1491
|
private classify;
|
|
1452
1492
|
/**
|
|
1453
|
-
* Returns the
|
|
1454
|
-
*
|
|
1455
|
-
*
|
|
1493
|
+
* Returns the agent that the current turn is locked to, if any. Flow-owner
|
|
1494
|
+
* (transient, set by stateful-agent activation) wins over user-pin (sticky,
|
|
1495
|
+
* set by picker click). `undefined` falls back to the classifier.
|
|
1496
|
+
*
|
|
1497
|
+
* Logs and returns `undefined` if a name no longer matches the agents array
|
|
1498
|
+
* — protects against stale state after an agents-array reassignment.
|
|
1456
1499
|
*/
|
|
1457
1500
|
private resolvePinnedAgent;
|
|
1458
1501
|
private appendInlineMessage;
|
|
@@ -24,7 +24,21 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
|
|
|
24
24
|
* so long-running `onActivate` work can bail if the session disconnects.
|
|
25
25
|
*/
|
|
26
26
|
private readonly lifecycleAbortController;
|
|
27
|
+
/**
|
|
28
|
+
* Sticky user pick from the picker (or the host's `setAgent` API). Only
|
|
29
|
+
* changes on explicit user action. Survives flow completion: when a stateful
|
|
30
|
+
* agent releases, the user-pin stays put and routing falls back to it on
|
|
31
|
+
* the next turn.
|
|
32
|
+
*/
|
|
27
33
|
private pinnedAgentName;
|
|
34
|
+
/**
|
|
35
|
+
* Transient flow lock — set when a stateful agent's `onActivate` fires,
|
|
36
|
+
* cleared by `releaseActiveAgent`. While non-null, routing precedence goes
|
|
37
|
+
* to this agent and the picker is locked. Independent of
|
|
38
|
+
* `pinnedAgentName` so completing a flow doesn't wipe the user's sticky
|
|
39
|
+
* choice.
|
|
40
|
+
*/
|
|
41
|
+
private flowOwnerAgentName;
|
|
28
42
|
activeAgent?: AgentConfig;
|
|
29
43
|
constructor(aiProvider: AIProvider, agents: AgentConfig[], options?: {
|
|
30
44
|
sessionKey?: string;
|
|
@@ -44,6 +58,13 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
|
|
|
44
58
|
* return to automatic routing.
|
|
45
59
|
*/
|
|
46
60
|
setPinnedAgent(name: string | null): void;
|
|
61
|
+
/**
|
|
62
|
+
* Set the flow-owner lock — used to restore state from Redux on driver
|
|
63
|
+
* reconstruction (agents-array changes, pop-in/pop-out). Not intended for
|
|
64
|
+
* external callers to manipulate routing; that's what `setPinnedAgent` is
|
|
65
|
+
* for.
|
|
66
|
+
*/
|
|
67
|
+
setFlowOwner(name: string | null): void;
|
|
47
68
|
loadHistory(messages: ChatMessage[]): void;
|
|
48
69
|
getRawHistory(): readonly ChatMessage[];
|
|
49
70
|
/** Delegates to the inner {@link ChatDriver} — turns are captured there. */
|
|
@@ -53,9 +74,15 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
|
|
|
53
74
|
continueFromHistory(transientPrimer?: ChatMessage[]): Promise<ChatDriverResult>;
|
|
54
75
|
private applyAgent;
|
|
55
76
|
/**
|
|
56
|
-
* Release the current stateful agent: fire `onDeactivate`, clear the
|
|
57
|
-
* dispatch events so the host (and Redux) reflect the
|
|
58
|
-
* automatically when a tool handler invokes
|
|
77
|
+
* Release the current stateful agent: fire `onDeactivate`, clear the
|
|
78
|
+
* flow-owner lock, dispatch events so the host (and Redux) reflect the
|
|
79
|
+
* unlocked state. Called automatically when a tool handler invokes
|
|
80
|
+
* `context.releaseAgent`.
|
|
81
|
+
*
|
|
82
|
+
* The user-pin (`pinnedAgentName`) is intentionally *not* cleared — a user
|
|
83
|
+
* who explicitly picked this agent should stay on it after the flow ends,
|
|
84
|
+
* so the next message starts a fresh flow on the same agent rather than
|
|
85
|
+
* routing through the classifier.
|
|
59
86
|
*/
|
|
60
87
|
private releaseActiveAgent;
|
|
61
88
|
/**
|
|
@@ -66,9 +93,12 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
|
|
|
66
93
|
dispose(): Promise<void>;
|
|
67
94
|
private classify;
|
|
68
95
|
/**
|
|
69
|
-
* Returns the
|
|
70
|
-
*
|
|
71
|
-
*
|
|
96
|
+
* Returns the agent that the current turn is locked to, if any. Flow-owner
|
|
97
|
+
* (transient, set by stateful-agent activation) wins over user-pin (sticky,
|
|
98
|
+
* set by picker click). `undefined` falls back to the classifier.
|
|
99
|
+
*
|
|
100
|
+
* Logs and returns `undefined` if a name no longer matches the agents array
|
|
101
|
+
* — protects against stale state after an agents-array reassignment.
|
|
72
102
|
*/
|
|
73
103
|
private resolvePinnedAgent;
|
|
74
104
|
private appendInlineMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrating-driver.d.ts","sourceRoot":"","sources":["../../../../src/components/orchestrating-driver/orchestrating-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,WAAW,EAEZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EACV,WAAW,EAKZ,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AAoDpC;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,WAAY,YAAW,QAAQ;
|
|
1
|
+
{"version":3,"file":"orchestrating-driver.d.ts","sourceRoot":"","sources":["../../../../src/components/orchestrating-driver/orchestrating-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,WAAW,EAEZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EACV,WAAW,EAKZ,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AAoDpC;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,WAAY,YAAW,QAAQ;IAgCpE,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAhCzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAsB;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAyB;IAClE;;;;;OAKG;IACH,OAAO,CAAC,eAAe,CAAuB;IAC9C;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB,CAAuB;IAEjD,WAAW,CAAC,EAAE,WAAW,CAAC;gBAGP,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,WAAW,EAAE,EACtC,OAAO,GAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,uBAAuB,CAAC,EAAE,MAAM,CAAC;QACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;KACtB;IA8DR,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAIhE,MAAM,IAAI,OAAO;IAIjB;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIzC;;;;;OAKG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIvC,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI;IAI1C,aAAa,IAAI,SAAS,WAAW,EAAE;IAIvC,4EAA4E;IAC5E,gBAAgB,IAAI,aAAa,CAAC,YAAY,CAAC;IAIzC,cAAc,CAClB,OAAO,EAAE,WAAW,EAAE,EACtB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,eAAe,EAAE,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC;IAgBd,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwErF,mBAAmB,CAAC,eAAe,CAAC,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAIvE,UAAU;IAuFxB;;;;;;;;;;OAUG;YACW,kBAAkB;IAuBhC;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAiBhB,QAAQ;IA+FtB;;;;;;;OAOG;IACH,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,mBAAmB;CAO5B"}
|
package/dist/dts/main/main.d.ts
CHANGED
|
@@ -100,9 +100,25 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
100
100
|
* Name of the agent the user has pinned via the agent picker. `null` means
|
|
101
101
|
* automatic routing (Auto). Persisted on the session store, so it survives
|
|
102
102
|
* pop-in/pop-out but resets on page refresh.
|
|
103
|
+
*
|
|
104
|
+
* Only changes on explicit user action (picker click or host's `setAgent`).
|
|
105
|
+
* The orchestrator's auto-pin on stateful activation writes
|
|
106
|
+
* `flowOwnerAgentName` instead, so finishing a guided flow doesn't wipe
|
|
107
|
+
* the user's sticky pick.
|
|
103
108
|
*/
|
|
104
109
|
get pinnedAgentName(): string | null;
|
|
105
110
|
set pinnedAgentName(value: string | null);
|
|
111
|
+
/**
|
|
112
|
+
* Name of the agent that currently owns a guided flow. Set by the
|
|
113
|
+
* orchestrator when a stateful agent's `onActivate` fires, cleared when the
|
|
114
|
+
* agent calls `releaseAgent` from a terminal-state tool handler. While
|
|
115
|
+
* non-null, routing precedence goes to this agent and the picker is locked.
|
|
116
|
+
*
|
|
117
|
+
* Independent of `pinnedAgentName`: a flow can complete and release
|
|
118
|
+
* without disturbing the user's sticky picker choice.
|
|
119
|
+
*/
|
|
120
|
+
get flowOwnerAgentName(): string | null;
|
|
121
|
+
set flowOwnerAgentName(value: string | null);
|
|
106
122
|
get liveSubAgentTrace(): ChatMessage[];
|
|
107
123
|
set liveSubAgentTrace(value: ChatMessage[]);
|
|
108
124
|
get liveSubAgentName(): string | null;
|
|
@@ -274,16 +290,13 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
274
290
|
/** Hint text for the currently pinned agent, if any. Used in the toggle button tooltip. */
|
|
275
291
|
get pinnedAgentHint(): string | undefined;
|
|
276
292
|
/**
|
|
277
|
-
* The pin is locked when a stateful
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
* completes) rather than `pinnedAgentName` (set immediately on picker
|
|
285
|
-
* click). The serialized `activeAgent` strips lifecycle hooks, so we look
|
|
286
|
-
* up the live config from `this.agents` to check for them.
|
|
293
|
+
* The pin is locked when a stateful flow is mid-stream — i.e. some agent's
|
|
294
|
+
* `onActivate` has fired and it owns live state, and it hasn't yet called
|
|
295
|
+
* `releaseAgent`. Derives directly from `flowOwnerAgentName`, the
|
|
296
|
+
* single source of truth the orchestrator writes on activation and clears
|
|
297
|
+
* on release. Until the user sends their first message, a freshly pinned
|
|
298
|
+
* stateful agent is not yet active and the picker should remain free; the
|
|
299
|
+
* user might change their mind and unpin without anything to clean up.
|
|
287
300
|
*/
|
|
288
301
|
get pinLocked(): boolean;
|
|
289
302
|
/** Tooltip shown on the picker toggle button. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/main/main.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,4BAA4B,EAC5B,WAAW,EACZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAExD,OAAO,EAGL,cAAc,EAIf,MAAM,uBAAuB,CAAC;AAW/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAepD,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,cAAc,CAAC;AAiEtB;;;;;;;;;;;;;GAaG;AACH,qBAOa,qBAAsB,SAAQ,cAAc;IAC3C,UAAU,EAAG,UAAU,CAAC;IAExB,kBAAkB,EAAE,MAAM,CAAW;IACZ,WAAW,EAAE,MAAM,CAAuB;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC5C,WAAW,EAAE,MAAM,CAA0B;IACrD;;;;;OAKG;IACiC,UAAU,CAAC,EAAE,UAAU,CAAC;IAC5D;;;OAGG;IACS,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,UAAU,CAAM;IAExC;;;OAGG;IACH,IAAI,WAAW,IAAI,eAAe,CAEjC;IAED;;;;;;OAMG;IACH,IACI,sBAAsB,IAAI,OAAO,CAKpC;IAEW,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;IAC9C,0EAA0E;IACrB,UAAU,UAAS;IAIxE,OAAO,CAAC,WAAW,CAAC,CAAqB;IAEzC,IAAI,QAAQ,IAAI,WAAW,EAAE,CAE5B;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,EAGhC;IAED,IAAI,KAAK,IAAI,gBAAgB,CAE5B;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,gBAAgB,EAehC;IAED;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB;IAM/B;;;;;;;;OAQG;IACH,OAAO,CAAC,uBAAuB;IAQ/B,IAAI,WAAW,IAAI,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,SAAS,CAE/D;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAM7C;IAED,IAAI,gBAAgB,IAAI,gBAAgB,CAEvC;IACD,IAAI,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,EAE3C;IAED,iEAAiE;IACjE,IAAI,aAAa,IAAI,OAAO,CAE3B;IACD,IAAI,aAAa,CAAC,KAAK,EAAE,OAAO,EAE/B;IAED,qEAAqE;IACrE,IAAI,iBAAiB,IAAI,OAAO,CAE/B;IACD,IAAI,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAEnC;IAED,8EAA8E;IAC9E,IAAI,wBAAwB,IAAI,OAAO,CAEtC;IACD,IAAI,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAE1C;IAED,oCAAoC;IACpC,IAAI,iBAAiB,IAAI,oBAAoB,EAAE,CAE9C;IACD,IAAI,iBAAiB,CAAC,KAAK,EAAE,oBAAoB,EAAE,EAElD;IAED;;;OAGG;IACH,IAAI,eAAe,IAAI,OAAO,CAE7B;IACD,IAAI,eAAe,CAAC,KAAK,EAAE,OAAO,EAEjC;IAED
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../src/main/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -40,9 +40,20 @@ export interface AiAssistantSessionState {
|
|
|
40
40
|
/**
|
|
41
41
|
* Name of the agent the user has pinned via the picker. `null` means the
|
|
42
42
|
* orchestrator chooses each turn (Auto). Survives lifecycle events but
|
|
43
|
-
* resets on page refresh.
|
|
43
|
+
* resets on page refresh. Only changed by explicit user picker action (or
|
|
44
|
+
* the host's `setAgent` API) — never by the orchestrator's auto-pin on
|
|
45
|
+
* stateful activation. See {@link flowOwnerAgentName} for that.
|
|
44
46
|
*/
|
|
45
47
|
pinnedAgentName: string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Name of the agent that currently owns a guided flow — set by the
|
|
50
|
+
* orchestrator when a stateful agent's `onActivate` fires, cleared by
|
|
51
|
+
* `releaseAgent` or session teardown. While non-null, routing precedence
|
|
52
|
+
* goes to this agent and the picker is locked. Independent of
|
|
53
|
+
* {@link pinnedAgentName}: a flow can wrap up and release without
|
|
54
|
+
* disturbing the user's sticky picker choice.
|
|
55
|
+
*/
|
|
56
|
+
flowOwnerAgentName: string | null;
|
|
46
57
|
/**
|
|
47
58
|
* Whether the agent picker slide-out panel is currently open. Lives in the
|
|
48
59
|
* slice so the bubble-dialog and popout-panel instances stay in sync as the
|
|
@@ -78,6 +89,7 @@ export declare const aiAssistantSlice: import("@reduxjs/toolkit").Slice<AiAssist
|
|
|
78
89
|
setActiveModel(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<string | undefined>): void;
|
|
79
90
|
setActiveAgent(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<Omit<AgentConfig, "toolHandlers"> | undefined>): void;
|
|
80
91
|
setPinnedAgentName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<string | null>): void;
|
|
92
|
+
setFlowOwnerAgentName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<string | null>): void;
|
|
81
93
|
setAgentPickerOpen(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<boolean>): void;
|
|
82
94
|
setInputValue(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<string>): void;
|
|
83
95
|
setLiveSubAgentTrace(state: import("immer").WritableDraft<AiAssistantSessionState>, action: PayloadAction<ChatMessage[]>): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-assistant-slice.d.ts","sourceRoot":"","sources":["../../../src/state/ai-assistant-slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAEnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEnG;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,IAAI,EAAE,4BAA4B,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,wBAAwB,EAAE,OAAO,CAAC;IAClC,iBAAiB,EAAE,oBAAoB,EAAE,CAAC;IAC1C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,kEAAkE;IAClE,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,SAAS,CAAC;IAC3D
|
|
1
|
+
{"version":3,"file":"ai-assistant-slice.d.ts","sourceRoot":"","sources":["../../../src/state/ai-assistant-slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAEnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEnG;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,IAAI,EAAE,4BAA4B,CAAC;CACpC;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,wBAAwB,EAAE,OAAO,CAAC;IAClC,iBAAiB,EAAE,oBAAoB,EAAE,CAAC;IAC1C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,kEAAkE;IAClE,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,SAAS,CAAC;IAC3D;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;;;;OAOG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,iBAAiB,EAAE,WAAW,EAAE,CAAC;IACjC,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;;OAKG;IACH,cAAc,EAAE,aAAa,EAAE,CAAC;CACjC;AAED,eAAO,MAAM,mBAAmB,EAAE,uBAoBjC,CAAC;AAEF,eAAO,MAAM,gBAAgB;uFAIE,aAAa,CAAC,WAAW,EAAE,CAAC;oFAG/B,aAAa,CAAC,gBAAgB,CAAC;4FAGvB,aAAa,CAAC,OAAO,CAAC;gGAGlB,aAAa,CAAC,OAAO,CAAC;uGAGf,aAAa,CAAC,OAAO,CAAC;gGAG7B,aAAa,CAAC,oBAAoB,EAAE,CAAC;+FAGtC,aAAa,CAAC,gBAAgB,CAAC;4FAGlC,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC;2FAGlC,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC;6FAG/B,aAAa,CAAC,MAAM,CAAC;0FAGxB,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC;0FAGjC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,SAAS,CAAC;8FAGxD,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC;iGAGzB,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC;8FAG/B,aAAa,CAAC,OAAO,CAAC;yFAG3B,aAAa,CAAC,MAAM,CAAC;gGAGd,aAAa,CAAC,WAAW,EAAE,CAAC;+FAG7B,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC;4FAG/B,aAAa,CAAC,aAAa,CAAC;+FAGzB,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;oCAKlE,CAAC"}
|
|
@@ -14,6 +14,7 @@ declare function makeStore(devTools: boolean | undefined): import("@genesislcap/
|
|
|
14
14
|
setActiveModel(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<string | undefined>): void;
|
|
15
15
|
setActiveAgent(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<Omit<import("..").AgentConfig, "toolHandlers"> | undefined>): void;
|
|
16
16
|
setPinnedAgentName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<string | null>): void;
|
|
17
|
+
setFlowOwnerAgentName(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<string | null>): void;
|
|
17
18
|
setAgentPickerOpen(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<boolean>): void;
|
|
18
19
|
setInputValue(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<string>): void;
|
|
19
20
|
setLiveSubAgentTrace(state: import("immer").WritableDraft<AiAssistantSessionState>, action: import("@reduxjs/toolkit").PayloadAction<import("@genesislcap/foundation-ai").ChatMessage[]>): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../../src/state/session-store.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAE9B,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAIvD,iBAAS,SAAS,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS
|
|
1
|
+
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../../src/state/session-store.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAE9B,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAIvD,iBAAS,SAAS,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;wCAM/C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAMnF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,YAAY,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,CAAC"}
|
|
@@ -60,7 +60,21 @@ export class OrchestratingDriver extends EventTarget {
|
|
|
60
60
|
* so long-running `onActivate` work can bail if the session disconnects.
|
|
61
61
|
*/
|
|
62
62
|
this.lifecycleAbortController = new AbortController();
|
|
63
|
+
/**
|
|
64
|
+
* Sticky user pick from the picker (or the host's `setAgent` API). Only
|
|
65
|
+
* changes on explicit user action. Survives flow completion: when a stateful
|
|
66
|
+
* agent releases, the user-pin stays put and routing falls back to it on
|
|
67
|
+
* the next turn.
|
|
68
|
+
*/
|
|
63
69
|
this.pinnedAgentName = null;
|
|
70
|
+
/**
|
|
71
|
+
* Transient flow lock — set when a stateful agent's `onActivate` fires,
|
|
72
|
+
* cleared by `releaseActiveAgent`. While non-null, routing precedence goes
|
|
73
|
+
* to this agent and the picker is locked. Independent of
|
|
74
|
+
* `pinnedAgentName` so completing a flow doesn't wipe the user's sticky
|
|
75
|
+
* choice.
|
|
76
|
+
*/
|
|
77
|
+
this.flowOwnerAgentName = null;
|
|
64
78
|
this.sessionKey = (_a = options.sessionKey) !== null && _a !== void 0 ? _a : '';
|
|
65
79
|
this.maxHandoffs = (_b = options.maxHandoffs) !== null && _b !== void 0 ? _b : DEFAULT_MAX_HANDOFFS;
|
|
66
80
|
this.classifierHistoryLength =
|
|
@@ -112,6 +126,15 @@ export class OrchestratingDriver extends EventTarget {
|
|
|
112
126
|
setPinnedAgent(name) {
|
|
113
127
|
this.pinnedAgentName = name;
|
|
114
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Set the flow-owner lock — used to restore state from Redux on driver
|
|
131
|
+
* reconstruction (agents-array changes, pop-in/pop-out). Not intended for
|
|
132
|
+
* external callers to manipulate routing; that's what `setPinnedAgent` is
|
|
133
|
+
* for.
|
|
134
|
+
*/
|
|
135
|
+
setFlowOwner(name) {
|
|
136
|
+
this.flowOwnerAgentName = name;
|
|
137
|
+
}
|
|
115
138
|
loadHistory(messages) {
|
|
116
139
|
this.chatDriver.loadHistory(messages);
|
|
117
140
|
}
|
|
@@ -238,24 +261,30 @@ export class OrchestratingDriver extends EventTarget {
|
|
|
238
261
|
}
|
|
239
262
|
}
|
|
240
263
|
const hasLifecycleHooks = !!(agent.onActivate || agent.onDeactivate);
|
|
241
|
-
// Stateful agents auto-
|
|
242
|
-
// survives subsequent turns (the classifier would otherwise be
|
|
243
|
-
// route away mid-flow, tearing the machine down). Release happens
|
|
244
|
-
// agent calls `releaseAgent` from a terminal-state tool handler
|
|
245
|
-
// post-sendMessage check below.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
264
|
+
// Stateful agents auto-lock the flow on activation. The lock guarantees
|
|
265
|
+
// the machine survives subsequent turns (the classifier would otherwise be
|
|
266
|
+
// free to route away mid-flow, tearing the machine down). Release happens
|
|
267
|
+
// when the agent calls `releaseAgent` from a terminal-state tool handler
|
|
268
|
+
// — see the post-sendMessage check below. We write `flowOwnerAgentName`
|
|
269
|
+
// rather than `pinnedAgentName` so completing a flow doesn't wipe the
|
|
270
|
+
// user's sticky picker choice.
|
|
271
|
+
if (isSwitch && hasLifecycleHooks && this.flowOwnerAgentName !== agent.name) {
|
|
272
|
+
this.flowOwnerAgentName = agent.name;
|
|
273
|
+
this.dispatchEvent(new CustomEvent('flow-owner-changed', { detail: agent.name }));
|
|
249
274
|
}
|
|
250
|
-
// Terminal agents do not get the cross-agent handoff tool.
|
|
251
|
-
// • fallback
|
|
252
|
-
// • pinned
|
|
253
|
-
// •
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
|
|
275
|
+
// Terminal agents do not get the cross-agent handoff tool. Four cases:
|
|
276
|
+
// • fallback — already a leaf; handoff would loop
|
|
277
|
+
// • user-pinned — user explicitly selected this agent; do not auto-route away
|
|
278
|
+
// • flow-owner — a stateful flow is mid-stream; do not auto-route away
|
|
279
|
+
// • stateful — agents with lifecycle hooks own state for the duration of
|
|
280
|
+
// their flow. Initiating a handoff mid-flow would abandon
|
|
281
|
+
// that state with no clean exit and dump the user into the
|
|
282
|
+
// classifier mid-machine. Capture the tool loop until the
|
|
283
|
+
// user (or the agent itself, via `releaseAgent`) releases.
|
|
284
|
+
const isTerminal = isFallback(agent) ||
|
|
285
|
+
this.pinnedAgentName !== null ||
|
|
286
|
+
this.flowOwnerAgentName !== null ||
|
|
287
|
+
hasLifecycleHooks;
|
|
259
288
|
let agentToApply = agent;
|
|
260
289
|
if (!isTerminal) {
|
|
261
290
|
const declaredTools = agent.toolDefinitions;
|
|
@@ -280,9 +309,15 @@ export class OrchestratingDriver extends EventTarget {
|
|
|
280
309
|
});
|
|
281
310
|
}
|
|
282
311
|
/**
|
|
283
|
-
* Release the current stateful agent: fire `onDeactivate`, clear the
|
|
284
|
-
* dispatch events so the host (and Redux) reflect the
|
|
285
|
-
* automatically when a tool handler invokes
|
|
312
|
+
* Release the current stateful agent: fire `onDeactivate`, clear the
|
|
313
|
+
* flow-owner lock, dispatch events so the host (and Redux) reflect the
|
|
314
|
+
* unlocked state. Called automatically when a tool handler invokes
|
|
315
|
+
* `context.releaseAgent`.
|
|
316
|
+
*
|
|
317
|
+
* The user-pin (`pinnedAgentName`) is intentionally *not* cleared — a user
|
|
318
|
+
* who explicitly picked this agent should stay on it after the flow ends,
|
|
319
|
+
* so the next message starts a fresh flow on the same agent rather than
|
|
320
|
+
* routing through the classifier.
|
|
286
321
|
*/
|
|
287
322
|
releaseActiveAgent() {
|
|
288
323
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -302,9 +337,9 @@ export class OrchestratingDriver extends EventTarget {
|
|
|
302
337
|
}
|
|
303
338
|
}
|
|
304
339
|
this.activeAgent = undefined;
|
|
305
|
-
if (this.
|
|
306
|
-
this.
|
|
307
|
-
this.dispatchEvent(new CustomEvent('
|
|
340
|
+
if (this.flowOwnerAgentName !== null) {
|
|
341
|
+
this.flowOwnerAgentName = null;
|
|
342
|
+
this.dispatchEvent(new CustomEvent('flow-owner-changed', { detail: null }));
|
|
308
343
|
}
|
|
309
344
|
this.dispatchEvent(new CustomEvent('agent-released', { detail: agent }));
|
|
310
345
|
this.dispatchEvent(new CustomEvent('agent-changed', { detail: undefined }));
|
|
@@ -418,16 +453,21 @@ export class OrchestratingDriver extends EventTarget {
|
|
|
418
453
|
});
|
|
419
454
|
}
|
|
420
455
|
/**
|
|
421
|
-
* Returns the
|
|
422
|
-
*
|
|
423
|
-
*
|
|
456
|
+
* Returns the agent that the current turn is locked to, if any. Flow-owner
|
|
457
|
+
* (transient, set by stateful-agent activation) wins over user-pin (sticky,
|
|
458
|
+
* set by picker click). `undefined` falls back to the classifier.
|
|
459
|
+
*
|
|
460
|
+
* Logs and returns `undefined` if a name no longer matches the agents array
|
|
461
|
+
* — protects against stale state after an agents-array reassignment.
|
|
424
462
|
*/
|
|
425
463
|
resolvePinnedAgent() {
|
|
426
|
-
|
|
464
|
+
var _a;
|
|
465
|
+
const name = (_a = this.flowOwnerAgentName) !== null && _a !== void 0 ? _a : this.pinnedAgentName;
|
|
466
|
+
if (name === null)
|
|
427
467
|
return undefined;
|
|
428
|
-
const match = this.agents.find((a) => a.name ===
|
|
468
|
+
const match = this.agents.find((a) => a.name === name);
|
|
429
469
|
if (!match) {
|
|
430
|
-
logger.warn(`OrchestratingDriver: pinned agent "${
|
|
470
|
+
logger.warn(`OrchestratingDriver: pinned agent "${name}" not found in agents array — falling back to classifier.`);
|
|
431
471
|
return undefined;
|
|
432
472
|
}
|
|
433
473
|
return match;
|
package/dist/esm/main/main.js
CHANGED
|
@@ -303,6 +303,11 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
303
303
|
* Name of the agent the user has pinned via the agent picker. `null` means
|
|
304
304
|
* automatic routing (Auto). Persisted on the session store, so it survives
|
|
305
305
|
* pop-in/pop-out but resets on page refresh.
|
|
306
|
+
*
|
|
307
|
+
* Only changes on explicit user action (picker click or host's `setAgent`).
|
|
308
|
+
* The orchestrator's auto-pin on stateful activation writes
|
|
309
|
+
* `flowOwnerAgentName` instead, so finishing a guided flow doesn't wipe
|
|
310
|
+
* the user's sticky pick.
|
|
306
311
|
*/
|
|
307
312
|
get pinnedAgentName() {
|
|
308
313
|
var _a, _b;
|
|
@@ -322,6 +327,26 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
322
327
|
this.fetchSuggestions();
|
|
323
328
|
}
|
|
324
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Name of the agent that currently owns a guided flow. Set by the
|
|
332
|
+
* orchestrator when a stateful agent's `onActivate` fires, cleared when the
|
|
333
|
+
* agent calls `releaseAgent` from a terminal-state tool handler. While
|
|
334
|
+
* non-null, routing precedence goes to this agent and the picker is locked.
|
|
335
|
+
*
|
|
336
|
+
* Independent of `pinnedAgentName`: a flow can complete and release
|
|
337
|
+
* without disturbing the user's sticky picker choice.
|
|
338
|
+
*/
|
|
339
|
+
get flowOwnerAgentName() {
|
|
340
|
+
var _a, _b;
|
|
341
|
+
return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.flowOwnerAgentName) !== null && _b !== void 0 ? _b : null;
|
|
342
|
+
}
|
|
343
|
+
set flowOwnerAgentName(value) {
|
|
344
|
+
var _a;
|
|
345
|
+
(_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setFlowOwnerAgentName(value);
|
|
346
|
+
if (this.driver instanceof OrchestratingDriver) {
|
|
347
|
+
this.driver.setFlowOwner(value);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
325
350
|
get liveSubAgentTrace() {
|
|
326
351
|
var _a, _b;
|
|
327
352
|
return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.liveSubAgentTrace) !== null && _b !== void 0 ? _b : [];
|
|
@@ -517,6 +542,13 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
517
542
|
if (oldDriver && 'dispose' in oldDriver && typeof oldDriver.dispose === 'function') {
|
|
518
543
|
void oldDriver.dispose();
|
|
519
544
|
}
|
|
545
|
+
// Clear any in-flight flow-owner lock from Redux too. The old machine has
|
|
546
|
+
// been disposed; we don't want the new driver re-locking to a flow that
|
|
547
|
+
// no longer has state behind it. User-pin survives — it's the user's
|
|
548
|
+
// sticky picker choice, not flow state.
|
|
549
|
+
if (this.flowOwnerAgentName !== null) {
|
|
550
|
+
this.flowOwnerAgentName = null;
|
|
551
|
+
}
|
|
520
552
|
this.driver = getOrCreateDriver(key, () => this.createDriver());
|
|
521
553
|
this.wireDriver();
|
|
522
554
|
if (history.length)
|
|
@@ -679,9 +711,11 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
679
711
|
() => driver.removeEventListener('interaction-stop', onInteractionStop),
|
|
680
712
|
];
|
|
681
713
|
if (driver instanceof OrchestratingDriver) {
|
|
682
|
-
// Restore
|
|
683
|
-
// driver, so pop-in/pop-out and agents-array
|
|
714
|
+
// Restore the user pin and flow-owner lock from the session store onto
|
|
715
|
+
// the freshly built driver, so pop-in/pop-out and agents-array
|
|
716
|
+
// reassignment preserve both.
|
|
684
717
|
driver.setPinnedAgent(this.pinnedAgentName);
|
|
718
|
+
driver.setFlowOwner(this.flowOwnerAgentName);
|
|
685
719
|
const onOrchStart = () => {
|
|
686
720
|
this.showHalo = 'orchestrating';
|
|
687
721
|
};
|
|
@@ -692,10 +726,12 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
692
726
|
const onAgentChanged = (e) => {
|
|
693
727
|
this.activeAgent = e.detail;
|
|
694
728
|
};
|
|
695
|
-
//
|
|
696
|
-
//
|
|
697
|
-
//
|
|
698
|
-
//
|
|
729
|
+
// User-initiated pin changes flow into the driver via the
|
|
730
|
+
// `pinnedAgentName` setter, which already calls setPinnedAgent — no
|
|
731
|
+
// event-driven mirror needed in this direction. The driver does still
|
|
732
|
+
// emit `'pinned-changed'` if other code ever calls `setPinnedAgent`
|
|
733
|
+
// directly, so keep the mirror for safety; guard against the redundant
|
|
734
|
+
// round-trip caused by our own setter call.
|
|
699
735
|
const onPinnedChanged = (e) => {
|
|
700
736
|
var _a, _b;
|
|
701
737
|
const name = e.detail;
|
|
@@ -703,11 +739,22 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
703
739
|
(_b = this._sessionRef) === null || _b === void 0 ? void 0 : _b.actions.aiAssistant.setPinnedAgentName(name);
|
|
704
740
|
}
|
|
705
741
|
};
|
|
742
|
+
// Orchestrator-initiated flow-owner changes (auto-lock on stateful
|
|
743
|
+
// activation, auto-clear on release). Mirror to Redux so the UI freeze
|
|
744
|
+
// state and `pinLocked` survive component remount.
|
|
745
|
+
const onFlowOwnerChanged = (e) => {
|
|
746
|
+
var _a, _b;
|
|
747
|
+
const name = e.detail;
|
|
748
|
+
if (((_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.flowOwnerAgentName) !== name) {
|
|
749
|
+
(_b = this._sessionRef) === null || _b === void 0 ? void 0 : _b.actions.aiAssistant.setFlowOwnerAgentName(name);
|
|
750
|
+
}
|
|
751
|
+
};
|
|
706
752
|
driver.addEventListener('orchestrating-start', onOrchStart);
|
|
707
753
|
driver.addEventListener('orchestrating-stop', onOrchStop);
|
|
708
754
|
driver.addEventListener('agent-changed', onAgentChanged);
|
|
709
755
|
driver.addEventListener('pinned-changed', onPinnedChanged);
|
|
710
|
-
|
|
756
|
+
driver.addEventListener('flow-owner-changed', onFlowOwnerChanged);
|
|
757
|
+
cleanups.push(() => driver.removeEventListener('orchestrating-start', onOrchStart), () => driver.removeEventListener('orchestrating-stop', onOrchStop), () => driver.removeEventListener('agent-changed', onAgentChanged), () => driver.removeEventListener('pinned-changed', onPinnedChanged), () => driver.removeEventListener('flow-owner-changed', onFlowOwnerChanged));
|
|
711
758
|
}
|
|
712
759
|
this.driverCleanup = () => {
|
|
713
760
|
for (const fn of cleanups)
|
|
@@ -1024,25 +1071,16 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
1024
1071
|
return (_c = (_b = (_a = this.agents) === null || _a === void 0 ? void 0 : _a.find((a) => a.name === this.pinnedAgentName)) === null || _b === void 0 ? void 0 : _b.manualSelection) === null || _c === void 0 ? void 0 : _c.hint;
|
|
1025
1072
|
}
|
|
1026
1073
|
/**
|
|
1027
|
-
* The pin is locked when a stateful
|
|
1028
|
-
*
|
|
1029
|
-
*
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1032
|
-
*
|
|
1033
|
-
*
|
|
1034
|
-
* completes) rather than `pinnedAgentName` (set immediately on picker
|
|
1035
|
-
* click). The serialized `activeAgent` strips lifecycle hooks, so we look
|
|
1036
|
-
* up the live config from `this.agents` to check for them.
|
|
1074
|
+
* The pin is locked when a stateful flow is mid-stream — i.e. some agent's
|
|
1075
|
+
* `onActivate` has fired and it owns live state, and it hasn't yet called
|
|
1076
|
+
* `releaseAgent`. Derives directly from `flowOwnerAgentName`, the
|
|
1077
|
+
* single source of truth the orchestrator writes on activation and clears
|
|
1078
|
+
* on release. Until the user sends their first message, a freshly pinned
|
|
1079
|
+
* stateful agent is not yet active and the picker should remain free; the
|
|
1080
|
+
* user might change their mind and unpin without anything to clean up.
|
|
1037
1081
|
*/
|
|
1038
1082
|
get pinLocked() {
|
|
1039
|
-
|
|
1040
|
-
if (!this.activeAgent)
|
|
1041
|
-
return false;
|
|
1042
|
-
const live = (_a = this.agents) === null || _a === void 0 ? void 0 : _a.find((a) => a.name === this.activeAgent.name);
|
|
1043
|
-
if (!live)
|
|
1044
|
-
return false;
|
|
1045
|
-
return !!(live.onActivate || live.onDeactivate);
|
|
1083
|
+
return this.flowOwnerAgentName !== null;
|
|
1046
1084
|
}
|
|
1047
1085
|
/** Tooltip shown on the picker toggle button. */
|
|
1048
1086
|
get agentToggleTitle() {
|
|
@@ -13,6 +13,7 @@ export const defaultSessionState = {
|
|
|
13
13
|
activeModel: undefined,
|
|
14
14
|
activeAgent: undefined,
|
|
15
15
|
pinnedAgentName: null,
|
|
16
|
+
flowOwnerAgentName: null,
|
|
16
17
|
agentPickerOpen: false,
|
|
17
18
|
inputValue: '',
|
|
18
19
|
liveSubAgentTrace: [],
|
|
@@ -62,6 +63,9 @@ export const aiAssistantSlice = createSlice({
|
|
|
62
63
|
setPinnedAgentName(state, action) {
|
|
63
64
|
state.pinnedAgentName = action.payload;
|
|
64
65
|
},
|
|
66
|
+
setFlowOwnerAgentName(state, action) {
|
|
67
|
+
state.flowOwnerAgentName = action.payload;
|
|
68
|
+
},
|
|
65
69
|
setAgentPickerOpen(state, action) {
|
|
66
70
|
state.agentPickerOpen = action.payload;
|
|
67
71
|
},
|
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": "14.444.
|
|
4
|
+
"version": "14.444.1",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/ai-assistant.d.ts",
|
|
@@ -64,24 +64,24 @@
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@genesislcap/foundation-testing": "14.444.
|
|
68
|
-
"@genesislcap/genx": "14.444.
|
|
69
|
-
"@genesislcap/rollup-builder": "14.444.
|
|
70
|
-
"@genesislcap/ts-builder": "14.444.
|
|
71
|
-
"@genesislcap/uvu-playwright-builder": "14.444.
|
|
72
|
-
"@genesislcap/vite-builder": "14.444.
|
|
73
|
-
"@genesislcap/webpack-builder": "14.444.
|
|
67
|
+
"@genesislcap/foundation-testing": "14.444.1",
|
|
68
|
+
"@genesislcap/genx": "14.444.1",
|
|
69
|
+
"@genesislcap/rollup-builder": "14.444.1",
|
|
70
|
+
"@genesislcap/ts-builder": "14.444.1",
|
|
71
|
+
"@genesislcap/uvu-playwright-builder": "14.444.1",
|
|
72
|
+
"@genesislcap/vite-builder": "14.444.1",
|
|
73
|
+
"@genesislcap/webpack-builder": "14.444.1",
|
|
74
74
|
"@types/dompurify": "^3.0.5",
|
|
75
75
|
"@types/marked": "^5.0.2"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@genesislcap/foundation-ai": "14.444.
|
|
79
|
-
"@genesislcap/foundation-logger": "14.444.
|
|
80
|
-
"@genesislcap/foundation-redux": "14.444.
|
|
81
|
-
"@genesislcap/foundation-ui": "14.444.
|
|
82
|
-
"@genesislcap/foundation-utils": "14.444.
|
|
83
|
-
"@genesislcap/rapid-design-system": "14.444.
|
|
84
|
-
"@genesislcap/web-core": "14.444.
|
|
78
|
+
"@genesislcap/foundation-ai": "14.444.1",
|
|
79
|
+
"@genesislcap/foundation-logger": "14.444.1",
|
|
80
|
+
"@genesislcap/foundation-redux": "14.444.1",
|
|
81
|
+
"@genesislcap/foundation-ui": "14.444.1",
|
|
82
|
+
"@genesislcap/foundation-utils": "14.444.1",
|
|
83
|
+
"@genesislcap/rapid-design-system": "14.444.1",
|
|
84
|
+
"@genesislcap/web-core": "14.444.1",
|
|
85
85
|
"dompurify": "^3.3.1",
|
|
86
86
|
"marked": "^17.0.3"
|
|
87
87
|
},
|
|
@@ -93,5 +93,5 @@
|
|
|
93
93
|
"publishConfig": {
|
|
94
94
|
"access": "public"
|
|
95
95
|
},
|
|
96
|
-
"gitHead": "
|
|
96
|
+
"gitHead": "837befe80b4972686cb48fe7d86334c5d819ced6"
|
|
97
97
|
}
|
|
@@ -91,7 +91,21 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
|
|
|
91
91
|
* so long-running `onActivate` work can bail if the session disconnects.
|
|
92
92
|
*/
|
|
93
93
|
private readonly lifecycleAbortController = new AbortController();
|
|
94
|
+
/**
|
|
95
|
+
* Sticky user pick from the picker (or the host's `setAgent` API). Only
|
|
96
|
+
* changes on explicit user action. Survives flow completion: when a stateful
|
|
97
|
+
* agent releases, the user-pin stays put and routing falls back to it on
|
|
98
|
+
* the next turn.
|
|
99
|
+
*/
|
|
94
100
|
private pinnedAgentName: string | null = null;
|
|
101
|
+
/**
|
|
102
|
+
* Transient flow lock — set when a stateful agent's `onActivate` fires,
|
|
103
|
+
* cleared by `releaseActiveAgent`. While non-null, routing precedence goes
|
|
104
|
+
* to this agent and the picker is locked. Independent of
|
|
105
|
+
* `pinnedAgentName` so completing a flow doesn't wipe the user's sticky
|
|
106
|
+
* choice.
|
|
107
|
+
*/
|
|
108
|
+
private flowOwnerAgentName: string | null = null;
|
|
95
109
|
|
|
96
110
|
activeAgent?: AgentConfig;
|
|
97
111
|
|
|
@@ -186,6 +200,16 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
|
|
|
186
200
|
this.pinnedAgentName = name;
|
|
187
201
|
}
|
|
188
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Set the flow-owner lock — used to restore state from Redux on driver
|
|
205
|
+
* reconstruction (agents-array changes, pop-in/pop-out). Not intended for
|
|
206
|
+
* external callers to manipulate routing; that's what `setPinnedAgent` is
|
|
207
|
+
* for.
|
|
208
|
+
*/
|
|
209
|
+
setFlowOwner(name: string | null): void {
|
|
210
|
+
this.flowOwnerAgentName = name;
|
|
211
|
+
}
|
|
212
|
+
|
|
189
213
|
loadHistory(messages: ChatMessage[]): void {
|
|
190
214
|
this.chatDriver.loadHistory(messages);
|
|
191
215
|
}
|
|
@@ -329,25 +353,32 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
|
|
|
329
353
|
|
|
330
354
|
const hasLifecycleHooks = !!(agent.onActivate || agent.onDeactivate);
|
|
331
355
|
|
|
332
|
-
// Stateful agents auto-
|
|
333
|
-
// survives subsequent turns (the classifier would otherwise be
|
|
334
|
-
// route away mid-flow, tearing the machine down). Release happens
|
|
335
|
-
// agent calls `releaseAgent` from a terminal-state tool handler
|
|
336
|
-
// post-sendMessage check below.
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
356
|
+
// Stateful agents auto-lock the flow on activation. The lock guarantees
|
|
357
|
+
// the machine survives subsequent turns (the classifier would otherwise be
|
|
358
|
+
// free to route away mid-flow, tearing the machine down). Release happens
|
|
359
|
+
// when the agent calls `releaseAgent` from a terminal-state tool handler
|
|
360
|
+
// — see the post-sendMessage check below. We write `flowOwnerAgentName`
|
|
361
|
+
// rather than `pinnedAgentName` so completing a flow doesn't wipe the
|
|
362
|
+
// user's sticky picker choice.
|
|
363
|
+
if (isSwitch && hasLifecycleHooks && this.flowOwnerAgentName !== agent.name) {
|
|
364
|
+
this.flowOwnerAgentName = agent.name;
|
|
365
|
+
this.dispatchEvent(new CustomEvent('flow-owner-changed', { detail: agent.name }));
|
|
340
366
|
}
|
|
341
367
|
|
|
342
|
-
// Terminal agents do not get the cross-agent handoff tool.
|
|
343
|
-
// • fallback
|
|
344
|
-
// • pinned
|
|
345
|
-
// •
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
|
|
368
|
+
// Terminal agents do not get the cross-agent handoff tool. Four cases:
|
|
369
|
+
// • fallback — already a leaf; handoff would loop
|
|
370
|
+
// • user-pinned — user explicitly selected this agent; do not auto-route away
|
|
371
|
+
// • flow-owner — a stateful flow is mid-stream; do not auto-route away
|
|
372
|
+
// • stateful — agents with lifecycle hooks own state for the duration of
|
|
373
|
+
// their flow. Initiating a handoff mid-flow would abandon
|
|
374
|
+
// that state with no clean exit and dump the user into the
|
|
375
|
+
// classifier mid-machine. Capture the tool loop until the
|
|
376
|
+
// user (or the agent itself, via `releaseAgent`) releases.
|
|
377
|
+
const isTerminal =
|
|
378
|
+
isFallback(agent) ||
|
|
379
|
+
this.pinnedAgentName !== null ||
|
|
380
|
+
this.flowOwnerAgentName !== null ||
|
|
381
|
+
hasLifecycleHooks;
|
|
351
382
|
|
|
352
383
|
let agentToApply: AgentConfig = agent;
|
|
353
384
|
if (!isTerminal) {
|
|
@@ -377,9 +408,15 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
|
|
|
377
408
|
}
|
|
378
409
|
|
|
379
410
|
/**
|
|
380
|
-
* Release the current stateful agent: fire `onDeactivate`, clear the
|
|
381
|
-
* dispatch events so the host (and Redux) reflect the
|
|
382
|
-
* automatically when a tool handler invokes
|
|
411
|
+
* Release the current stateful agent: fire `onDeactivate`, clear the
|
|
412
|
+
* flow-owner lock, dispatch events so the host (and Redux) reflect the
|
|
413
|
+
* unlocked state. Called automatically when a tool handler invokes
|
|
414
|
+
* `context.releaseAgent`.
|
|
415
|
+
*
|
|
416
|
+
* The user-pin (`pinnedAgentName`) is intentionally *not* cleared — a user
|
|
417
|
+
* who explicitly picked this agent should stay on it after the flow ends,
|
|
418
|
+
* so the next message starts a fresh flow on the same agent rather than
|
|
419
|
+
* routing through the classifier.
|
|
383
420
|
*/
|
|
384
421
|
private async releaseActiveAgent(): Promise<void> {
|
|
385
422
|
const agent = this.activeAgent;
|
|
@@ -396,9 +433,9 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
|
|
|
396
433
|
}
|
|
397
434
|
}
|
|
398
435
|
this.activeAgent = undefined;
|
|
399
|
-
if (this.
|
|
400
|
-
this.
|
|
401
|
-
this.dispatchEvent(new CustomEvent('
|
|
436
|
+
if (this.flowOwnerAgentName !== null) {
|
|
437
|
+
this.flowOwnerAgentName = null;
|
|
438
|
+
this.dispatchEvent(new CustomEvent('flow-owner-changed', { detail: null }));
|
|
402
439
|
}
|
|
403
440
|
this.dispatchEvent(new CustomEvent('agent-released', { detail: agent }));
|
|
404
441
|
this.dispatchEvent(new CustomEvent('agent-changed', { detail: undefined }));
|
|
@@ -522,16 +559,20 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
|
|
|
522
559
|
}
|
|
523
560
|
|
|
524
561
|
/**
|
|
525
|
-
* Returns the
|
|
526
|
-
*
|
|
527
|
-
*
|
|
562
|
+
* Returns the agent that the current turn is locked to, if any. Flow-owner
|
|
563
|
+
* (transient, set by stateful-agent activation) wins over user-pin (sticky,
|
|
564
|
+
* set by picker click). `undefined` falls back to the classifier.
|
|
565
|
+
*
|
|
566
|
+
* Logs and returns `undefined` if a name no longer matches the agents array
|
|
567
|
+
* — protects against stale state after an agents-array reassignment.
|
|
528
568
|
*/
|
|
529
569
|
private resolvePinnedAgent(): AgentConfig | undefined {
|
|
530
|
-
|
|
531
|
-
|
|
570
|
+
const name = this.flowOwnerAgentName ?? this.pinnedAgentName;
|
|
571
|
+
if (name === null) return undefined;
|
|
572
|
+
const match = this.agents.find((a) => a.name === name);
|
|
532
573
|
if (!match) {
|
|
533
574
|
logger.warn(
|
|
534
|
-
`OrchestratingDriver: pinned agent "${
|
|
575
|
+
`OrchestratingDriver: pinned agent "${name}" not found in agents array — falling back to classifier.`,
|
|
535
576
|
);
|
|
536
577
|
return undefined;
|
|
537
578
|
}
|
package/src/main/main.ts
CHANGED
|
@@ -329,6 +329,11 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
329
329
|
* Name of the agent the user has pinned via the agent picker. `null` means
|
|
330
330
|
* automatic routing (Auto). Persisted on the session store, so it survives
|
|
331
331
|
* pop-in/pop-out but resets on page refresh.
|
|
332
|
+
*
|
|
333
|
+
* Only changes on explicit user action (picker click or host's `setAgent`).
|
|
334
|
+
* The orchestrator's auto-pin on stateful activation writes
|
|
335
|
+
* `flowOwnerAgentName` instead, so finishing a guided flow doesn't wipe
|
|
336
|
+
* the user's sticky pick.
|
|
332
337
|
*/
|
|
333
338
|
get pinnedAgentName(): string | null {
|
|
334
339
|
return this._sessionRef?.store.aiAssistant.pinnedAgentName ?? null;
|
|
@@ -347,6 +352,25 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
347
352
|
}
|
|
348
353
|
}
|
|
349
354
|
|
|
355
|
+
/**
|
|
356
|
+
* Name of the agent that currently owns a guided flow. Set by the
|
|
357
|
+
* orchestrator when a stateful agent's `onActivate` fires, cleared when the
|
|
358
|
+
* agent calls `releaseAgent` from a terminal-state tool handler. While
|
|
359
|
+
* non-null, routing precedence goes to this agent and the picker is locked.
|
|
360
|
+
*
|
|
361
|
+
* Independent of `pinnedAgentName`: a flow can complete and release
|
|
362
|
+
* without disturbing the user's sticky picker choice.
|
|
363
|
+
*/
|
|
364
|
+
get flowOwnerAgentName(): string | null {
|
|
365
|
+
return this._sessionRef?.store.aiAssistant.flowOwnerAgentName ?? null;
|
|
366
|
+
}
|
|
367
|
+
set flowOwnerAgentName(value: string | null) {
|
|
368
|
+
this._sessionRef?.actions.aiAssistant.setFlowOwnerAgentName(value);
|
|
369
|
+
if (this.driver instanceof OrchestratingDriver) {
|
|
370
|
+
this.driver.setFlowOwner(value);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
350
374
|
get liveSubAgentTrace(): ChatMessage[] {
|
|
351
375
|
return this._sessionRef?.store.aiAssistant.liveSubAgentTrace ?? [];
|
|
352
376
|
}
|
|
@@ -590,6 +614,13 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
590
614
|
if (oldDriver && 'dispose' in oldDriver && typeof oldDriver.dispose === 'function') {
|
|
591
615
|
void oldDriver.dispose();
|
|
592
616
|
}
|
|
617
|
+
// Clear any in-flight flow-owner lock from Redux too. The old machine has
|
|
618
|
+
// been disposed; we don't want the new driver re-locking to a flow that
|
|
619
|
+
// no longer has state behind it. User-pin survives — it's the user's
|
|
620
|
+
// sticky picker choice, not flow state.
|
|
621
|
+
if (this.flowOwnerAgentName !== null) {
|
|
622
|
+
this.flowOwnerAgentName = null;
|
|
623
|
+
}
|
|
593
624
|
this.driver = getOrCreateDriver(key, () => this.createDriver());
|
|
594
625
|
this.wireDriver();
|
|
595
626
|
if (history.length) this.driver.loadHistory([...history]);
|
|
@@ -766,9 +797,11 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
766
797
|
];
|
|
767
798
|
|
|
768
799
|
if (driver instanceof OrchestratingDriver) {
|
|
769
|
-
// Restore
|
|
770
|
-
// driver, so pop-in/pop-out and agents-array
|
|
800
|
+
// Restore the user pin and flow-owner lock from the session store onto
|
|
801
|
+
// the freshly built driver, so pop-in/pop-out and agents-array
|
|
802
|
+
// reassignment preserve both.
|
|
771
803
|
driver.setPinnedAgent(this.pinnedAgentName);
|
|
804
|
+
driver.setFlowOwner(this.flowOwnerAgentName);
|
|
772
805
|
const onOrchStart = () => {
|
|
773
806
|
this.showHalo = 'orchestrating';
|
|
774
807
|
};
|
|
@@ -778,25 +811,38 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
778
811
|
const onAgentChanged = (e: Event) => {
|
|
779
812
|
this.activeAgent = (e as CustomEvent<AgentConfig>).detail;
|
|
780
813
|
};
|
|
781
|
-
//
|
|
782
|
-
//
|
|
783
|
-
//
|
|
784
|
-
//
|
|
814
|
+
// User-initiated pin changes flow into the driver via the
|
|
815
|
+
// `pinnedAgentName` setter, which already calls setPinnedAgent — no
|
|
816
|
+
// event-driven mirror needed in this direction. The driver does still
|
|
817
|
+
// emit `'pinned-changed'` if other code ever calls `setPinnedAgent`
|
|
818
|
+
// directly, so keep the mirror for safety; guard against the redundant
|
|
819
|
+
// round-trip caused by our own setter call.
|
|
785
820
|
const onPinnedChanged = (e: Event) => {
|
|
786
821
|
const name = (e as CustomEvent<string | null>).detail;
|
|
787
822
|
if (this._sessionRef?.store.aiAssistant.pinnedAgentName !== name) {
|
|
788
823
|
this._sessionRef?.actions.aiAssistant.setPinnedAgentName(name);
|
|
789
824
|
}
|
|
790
825
|
};
|
|
826
|
+
// Orchestrator-initiated flow-owner changes (auto-lock on stateful
|
|
827
|
+
// activation, auto-clear on release). Mirror to Redux so the UI freeze
|
|
828
|
+
// state and `pinLocked` survive component remount.
|
|
829
|
+
const onFlowOwnerChanged = (e: Event) => {
|
|
830
|
+
const name = (e as CustomEvent<string | null>).detail;
|
|
831
|
+
if (this._sessionRef?.store.aiAssistant.flowOwnerAgentName !== name) {
|
|
832
|
+
this._sessionRef?.actions.aiAssistant.setFlowOwnerAgentName(name);
|
|
833
|
+
}
|
|
834
|
+
};
|
|
791
835
|
driver.addEventListener('orchestrating-start', onOrchStart);
|
|
792
836
|
driver.addEventListener('orchestrating-stop', onOrchStop);
|
|
793
837
|
driver.addEventListener('agent-changed', onAgentChanged);
|
|
794
838
|
driver.addEventListener('pinned-changed', onPinnedChanged);
|
|
839
|
+
driver.addEventListener('flow-owner-changed', onFlowOwnerChanged);
|
|
795
840
|
cleanups.push(
|
|
796
841
|
() => driver.removeEventListener('orchestrating-start', onOrchStart),
|
|
797
842
|
() => driver.removeEventListener('orchestrating-stop', onOrchStop),
|
|
798
843
|
() => driver.removeEventListener('agent-changed', onAgentChanged),
|
|
799
844
|
() => driver.removeEventListener('pinned-changed', onPinnedChanged),
|
|
845
|
+
() => driver.removeEventListener('flow-owner-changed', onFlowOwnerChanged),
|
|
800
846
|
);
|
|
801
847
|
}
|
|
802
848
|
|
|
@@ -1144,23 +1190,17 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1144
1190
|
}
|
|
1145
1191
|
|
|
1146
1192
|
/**
|
|
1147
|
-
* The pin is locked when a stateful
|
|
1148
|
-
*
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1154
|
-
* completes) rather than `pinnedAgentName` (set immediately on picker
|
|
1155
|
-
* click). The serialized `activeAgent` strips lifecycle hooks, so we look
|
|
1156
|
-
* up the live config from `this.agents` to check for them.
|
|
1193
|
+
* The pin is locked when a stateful flow is mid-stream — i.e. some agent's
|
|
1194
|
+
* `onActivate` has fired and it owns live state, and it hasn't yet called
|
|
1195
|
+
* `releaseAgent`. Derives directly from `flowOwnerAgentName`, the
|
|
1196
|
+
* single source of truth the orchestrator writes on activation and clears
|
|
1197
|
+
* on release. Until the user sends their first message, a freshly pinned
|
|
1198
|
+
* stateful agent is not yet active and the picker should remain free; the
|
|
1199
|
+
* user might change their mind and unpin without anything to clean up.
|
|
1157
1200
|
*/
|
|
1158
1201
|
@volatile
|
|
1159
1202
|
get pinLocked(): boolean {
|
|
1160
|
-
|
|
1161
|
-
const live = this.agents?.find((a) => a.name === this.activeAgent!.name);
|
|
1162
|
-
if (!live) return false;
|
|
1163
|
-
return !!(live.onActivate || live.onDeactivate);
|
|
1203
|
+
return this.flowOwnerAgentName !== null;
|
|
1164
1204
|
}
|
|
1165
1205
|
|
|
1166
1206
|
/** Tooltip shown on the picker toggle button. */
|
|
@@ -43,9 +43,20 @@ export interface AiAssistantSessionState {
|
|
|
43
43
|
/**
|
|
44
44
|
* Name of the agent the user has pinned via the picker. `null` means the
|
|
45
45
|
* orchestrator chooses each turn (Auto). Survives lifecycle events but
|
|
46
|
-
* resets on page refresh.
|
|
46
|
+
* resets on page refresh. Only changed by explicit user picker action (or
|
|
47
|
+
* the host's `setAgent` API) — never by the orchestrator's auto-pin on
|
|
48
|
+
* stateful activation. See {@link flowOwnerAgentName} for that.
|
|
47
49
|
*/
|
|
48
50
|
pinnedAgentName: string | null;
|
|
51
|
+
/**
|
|
52
|
+
* Name of the agent that currently owns a guided flow — set by the
|
|
53
|
+
* orchestrator when a stateful agent's `onActivate` fires, cleared by
|
|
54
|
+
* `releaseAgent` or session teardown. While non-null, routing precedence
|
|
55
|
+
* goes to this agent and the picker is locked. Independent of
|
|
56
|
+
* {@link pinnedAgentName}: a flow can wrap up and release without
|
|
57
|
+
* disturbing the user's sticky picker choice.
|
|
58
|
+
*/
|
|
59
|
+
flowOwnerAgentName: string | null;
|
|
49
60
|
/**
|
|
50
61
|
* Whether the agent picker slide-out panel is currently open. Lives in the
|
|
51
62
|
* slice so the bubble-dialog and popout-panel instances stay in sync as the
|
|
@@ -81,6 +92,7 @@ export const defaultSessionState: AiAssistantSessionState = {
|
|
|
81
92
|
activeModel: undefined,
|
|
82
93
|
activeAgent: undefined,
|
|
83
94
|
pinnedAgentName: null,
|
|
95
|
+
flowOwnerAgentName: null,
|
|
84
96
|
agentPickerOpen: false,
|
|
85
97
|
inputValue: '',
|
|
86
98
|
liveSubAgentTrace: [],
|
|
@@ -131,6 +143,9 @@ export const aiAssistantSlice = createSlice({
|
|
|
131
143
|
setPinnedAgentName(state, action: PayloadAction<string | null>) {
|
|
132
144
|
state.pinnedAgentName = action.payload;
|
|
133
145
|
},
|
|
146
|
+
setFlowOwnerAgentName(state, action: PayloadAction<string | null>) {
|
|
147
|
+
state.flowOwnerAgentName = action.payload;
|
|
148
|
+
},
|
|
134
149
|
setAgentPickerOpen(state, action: PayloadAction<boolean>) {
|
|
135
150
|
state.agentPickerOpen = action.payload;
|
|
136
151
|
},
|