@genesislcap/ai-assistant 14.493.1-alpha-8ff3328.0 → 14.495.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.
- package/dist/ai-assistant.api.json +12 -3
- package/dist/ai-assistant.d.ts +29 -1
- package/dist/custom-elements.json +76 -0
- package/dist/dts/channel/ai-activity-channel.d.ts +10 -1
- package/dist/dts/channel/ai-activity-channel.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +17 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.test.d.ts.map +1 -1
- package/dist/dts/index.d.ts +1 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/state/debug-event-log.d.ts +8 -12
- package/dist/dts/state/debug-event-log.d.ts.map +1 -1
- package/dist/esm/components/chat-driver/chat-driver.js +51 -13
- package/dist/esm/components/chat-driver/chat-driver.test.js +133 -1
- package/package.json +17 -17
- package/src/channel/ai-activity-channel.ts +9 -1
- package/src/components/chat-driver/chat-driver.test.ts +214 -1
- package/src/components/chat-driver/chat-driver.ts +57 -13
- package/src/index.ts +1 -0
- package/src/state/debug-event-log.ts +9 -18
|
@@ -856,7 +856,7 @@
|
|
|
856
856
|
{
|
|
857
857
|
"kind": "PropertySignature",
|
|
858
858
|
"canonicalReference": "@genesislcap/ai-assistant!AgenticActivityEvents#\"tool-loop-end\":member",
|
|
859
|
-
"docComment": "/**\n * Fired when the tool loop completes for the current turn and control returns to the user on the **main chat prompt** (i.e. the next free-form user message can now be accepted).\n *\n * Boundary: this is the moment the user regains control of the main chat input. It is **not** fired when a tool handler hands a widget to the user mid-loop (e.g. while a planning widget is awaiting a click) — widget pauses are still inside the loop, the assistant still owns the turn, and the main chat input may be hidden during them. Wait for this event when you need to know that the assistant is fully idle and the user can type freely again.\n *\n * Emitted from the finally block of each `ChatDriver`'s `sendMessage` and `continueFromHistory`, so a multi-agent handoff fires this event once per agent.\n *\n * Distinct from {@link AgenticActivityEvents.'halo-stop'} — the halo event tracks the spinner animation lifecycle (which can flap mid-turn as tool calls resolve), whereas this event is a one-shot per turn boundary. Use this for hooks tied to a turn ending (telemetry flushes, post-turn workspace transitions, deferred state recomputation).\n */\n",
|
|
859
|
+
"docComment": "/**\n * Fired when the tool loop completes for the current turn and control returns to the user on the **main chat prompt** (i.e. the next free-form user message can now be accepted).\n *\n * Boundary: this is the moment the user regains control of the main chat input. It is **not** fired when a tool handler hands a widget to the user mid-loop (e.g. while a planning widget is awaiting a click) — widget pauses are still inside the loop, the assistant still owns the turn, and the main chat input may be hidden during them. Wait for this event when you need to know that the assistant is fully idle and the user can type freely again.\n *\n * Emitted from the finally block of each `ChatDriver`'s `sendMessage` and `continueFromHistory`, so a multi-agent handoff fires this event once per agent.\n *\n * Distinct from {@link AgenticActivityEvents.'halo-stop'} — the halo event tracks the spinner animation lifecycle (which can flap mid-turn as tool calls resolve), whereas this event is a one-shot per turn boundary. Use this for hooks tied to a turn ending (telemetry flushes, post-turn workspace transitions, deferred state recomputation).\n *\n * The detail carries the turn's outcome (PTC-0): on a failure it is `{ failureReason }` with the typed `TurnFailureReason`; on a clean completion, a user cancel, a driver dispose, and an agent handoff the detail is `undefined` — the historical shape, kept byte-identical so subscribers that only care about the boundary can keep ignoring it. Structured-cloneable so it survives the cross-tab BroadcastChannel hop.\n */\n",
|
|
860
860
|
"excerptTokens": [
|
|
861
861
|
{
|
|
862
862
|
"kind": "Content",
|
|
@@ -864,7 +864,16 @@
|
|
|
864
864
|
},
|
|
865
865
|
{
|
|
866
866
|
"kind": "Content",
|
|
867
|
-
"text": "
|
|
867
|
+
"text": "{\n failureReason?: "
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
"kind": "Reference",
|
|
871
|
+
"text": "TurnFailureReason",
|
|
872
|
+
"canonicalReference": "@genesislcap/foundation-ai!TurnFailureReason:type"
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
"kind": "Content",
|
|
876
|
+
"text": ";\n } | undefined"
|
|
868
877
|
},
|
|
869
878
|
{
|
|
870
879
|
"kind": "Content",
|
|
@@ -877,7 +886,7 @@
|
|
|
877
886
|
"name": "\"tool-loop-end\"",
|
|
878
887
|
"propertyTypeTokenRange": {
|
|
879
888
|
"startIndex": 1,
|
|
880
|
-
"endIndex":
|
|
889
|
+
"endIndex": 4
|
|
881
890
|
}
|
|
882
891
|
},
|
|
883
892
|
{
|
package/dist/ai-assistant.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { InterfaceSymbol } from '@microsoft/fast-foundation';
|
|
|
18
18
|
import type { Modal } from '@genesislcap/rapid-design-system';
|
|
19
19
|
import { ModelTagAppearance } from '@genesislcap/foundation-ai';
|
|
20
20
|
import type { SubAgentFailureReason } from '@genesislcap/foundation-ai';
|
|
21
|
+
import type { TurnFailureReason } from '@genesislcap/foundation-ai';
|
|
21
22
|
import { ViewTemplate } from '@genesislcap/web-core';
|
|
22
23
|
|
|
23
24
|
/** Sentinel value used by the segmented control / select to represent "Auto". */
|
|
@@ -179,8 +180,16 @@ export declare interface AgenticActivityEvents {
|
|
|
179
180
|
* spinner animation lifecycle (which can flap mid-turn as tool calls resolve), whereas
|
|
180
181
|
* this event is a one-shot per turn boundary. Use this for hooks tied to a turn ending
|
|
181
182
|
* (telemetry flushes, post-turn workspace transitions, deferred state recomputation).
|
|
183
|
+
*
|
|
184
|
+
* The detail carries the turn's outcome (PTC-0): on a failure it is `{ failureReason }`
|
|
185
|
+
* with the typed `TurnFailureReason`; on a clean completion, a user cancel, a
|
|
186
|
+
* driver dispose, and an agent handoff the detail is `undefined` — the historical shape,
|
|
187
|
+
* kept byte-identical so subscribers that only care about the boundary can keep ignoring
|
|
188
|
+
* it. Structured-cloneable so it survives the cross-tab BroadcastChannel hop.
|
|
182
189
|
*/
|
|
183
|
-
'tool-loop-end':
|
|
190
|
+
'tool-loop-end': {
|
|
191
|
+
failureReason?: TurnFailureReason;
|
|
192
|
+
} | undefined;
|
|
184
193
|
/**
|
|
185
194
|
* Fired when a tool handler hands a widget to the user mid-loop and parks awaiting it
|
|
186
195
|
* (`requestInteraction`) — the turn is suspended between provider calls, with no request
|
|
@@ -1268,6 +1277,23 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1268
1277
|
* cached history would otherwise gain a stray marker on remount).
|
|
1269
1278
|
*/
|
|
1270
1279
|
private completeAbortedTurn;
|
|
1280
|
+
/**
|
|
1281
|
+
* Build the `done` loop result, carrying the typed failure reason when the turn
|
|
1282
|
+
* bailed (PTC-0). The discriminant stays `'done'` either way — the same value a
|
|
1283
|
+
* clean turn returns — so consumers matching on `reason === 'done'` are unchanged;
|
|
1284
|
+
* `failureReason` is simply present on a failure and absent on success. Omitted
|
|
1285
|
+
* (rather than set to `undefined`) so a happy-path result stays byte-identical to
|
|
1286
|
+
* the historical `{ reason: 'done' }`.
|
|
1287
|
+
*/
|
|
1288
|
+
private turnDone;
|
|
1289
|
+
/** The typed failure reason on a loop result, or `undefined` for a clean turn / handoff. */
|
|
1290
|
+
private static failureReasonOf;
|
|
1291
|
+
/**
|
|
1292
|
+
* Build the `tool-loop-end` event detail for a turn's result. A failure carries a
|
|
1293
|
+
* `{ failureReason }` detail; a clean turn emits `undefined` — the historical shape,
|
|
1294
|
+
* kept byte-identical so subscribers see exactly what they always have.
|
|
1295
|
+
*/
|
|
1296
|
+
private static loopEndDetail;
|
|
1271
1297
|
/**
|
|
1272
1298
|
* Swap in a new agent's configuration. Called by OrchestratingDriver before
|
|
1273
1299
|
* each specialist turn so the shared driver runs with the right tools and prompt.
|
|
@@ -3698,6 +3724,8 @@ export declare interface ToolTreeNode extends ChatToolDefinition {
|
|
|
3698
3724
|
tools?: ToolTreeNode[];
|
|
3699
3725
|
}
|
|
3700
3726
|
|
|
3727
|
+
export { TurnFailureReason }
|
|
3728
|
+
|
|
3701
3729
|
/**
|
|
3702
3730
|
* One captured frame of what the LLM saw on a single tool-loop iteration.
|
|
3703
3731
|
* The driver records these as a ring buffer (cap: configurable via
|
|
@@ -151,6 +151,14 @@
|
|
|
151
151
|
"package": "./utils/tool-fold"
|
|
152
152
|
}
|
|
153
153
|
},
|
|
154
|
+
{
|
|
155
|
+
"kind": "js",
|
|
156
|
+
"name": "TurnFailureReason",
|
|
157
|
+
"declaration": {
|
|
158
|
+
"name": "TurnFailureReason",
|
|
159
|
+
"module": "./state/debug-event-log"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
154
162
|
{
|
|
155
163
|
"kind": "js",
|
|
156
164
|
"name": "TimelineMessage",
|
|
@@ -3971,6 +3979,14 @@
|
|
|
3971
3979
|
"module": "src/state/debug-event-log.ts"
|
|
3972
3980
|
}
|
|
3973
3981
|
},
|
|
3982
|
+
{
|
|
3983
|
+
"kind": "js",
|
|
3984
|
+
"name": "TurnFailureReason",
|
|
3985
|
+
"declaration": {
|
|
3986
|
+
"name": "TurnFailureReason",
|
|
3987
|
+
"module": "src/state/debug-event-log.ts"
|
|
3988
|
+
}
|
|
3989
|
+
},
|
|
3974
3990
|
{
|
|
3975
3991
|
"kind": "js",
|
|
3976
3992
|
"name": "recordTurnError",
|
|
@@ -6892,6 +6908,66 @@
|
|
|
6892
6908
|
},
|
|
6893
6909
|
"description": "Finish a turn whose signal aborted. A user cancel adds a subtle \"Stopped.\"\nmarker; a dispose-driven abort stops quietly (the widget is gone and the\ncached history would otherwise gain a stray marker on remount)."
|
|
6894
6910
|
},
|
|
6911
|
+
{
|
|
6912
|
+
"kind": "method",
|
|
6913
|
+
"name": "turnDone",
|
|
6914
|
+
"privacy": "private",
|
|
6915
|
+
"return": {
|
|
6916
|
+
"type": {
|
|
6917
|
+
"text": "ChatDriverResult"
|
|
6918
|
+
}
|
|
6919
|
+
},
|
|
6920
|
+
"parameters": [
|
|
6921
|
+
{
|
|
6922
|
+
"name": "failureReason",
|
|
6923
|
+
"optional": true,
|
|
6924
|
+
"type": {
|
|
6925
|
+
"text": "TurnFailureReason"
|
|
6926
|
+
}
|
|
6927
|
+
}
|
|
6928
|
+
],
|
|
6929
|
+
"description": "Build the `done` loop result, carrying the typed failure reason when the turn\nbailed (PTC-0). The discriminant stays `'done'` either way — the same value a\nclean turn returns — so consumers matching on `reason === 'done'` are unchanged;\n`failureReason` is simply present on a failure and absent on success. Omitted\n(rather than set to `undefined`) so a happy-path result stays byte-identical to\nthe historical `{ reason: 'done' }`."
|
|
6930
|
+
},
|
|
6931
|
+
{
|
|
6932
|
+
"kind": "method",
|
|
6933
|
+
"name": "failureReasonOf",
|
|
6934
|
+
"privacy": "private",
|
|
6935
|
+
"static": true,
|
|
6936
|
+
"return": {
|
|
6937
|
+
"type": {
|
|
6938
|
+
"text": "TurnFailureReason | undefined"
|
|
6939
|
+
}
|
|
6940
|
+
},
|
|
6941
|
+
"parameters": [
|
|
6942
|
+
{
|
|
6943
|
+
"name": "result",
|
|
6944
|
+
"type": {
|
|
6945
|
+
"text": "ChatDriverResult"
|
|
6946
|
+
}
|
|
6947
|
+
}
|
|
6948
|
+
],
|
|
6949
|
+
"description": "The typed failure reason on a loop result, or `undefined` for a clean turn / handoff."
|
|
6950
|
+
},
|
|
6951
|
+
{
|
|
6952
|
+
"kind": "method",
|
|
6953
|
+
"name": "loopEndDetail",
|
|
6954
|
+
"privacy": "private",
|
|
6955
|
+
"static": true,
|
|
6956
|
+
"return": {
|
|
6957
|
+
"type": {
|
|
6958
|
+
"text": "{ failureReason: TurnFailureReason } | undefined"
|
|
6959
|
+
}
|
|
6960
|
+
},
|
|
6961
|
+
"parameters": [
|
|
6962
|
+
{
|
|
6963
|
+
"name": "result",
|
|
6964
|
+
"type": {
|
|
6965
|
+
"text": "ChatDriverResult"
|
|
6966
|
+
}
|
|
6967
|
+
}
|
|
6968
|
+
],
|
|
6969
|
+
"description": "Build the `tool-loop-end` event detail for a turn's result. A failure carries a\n`{ failureReason }` detail; a clean turn emits `undefined` — the historical shape,\nkept byte-identical so subscribers see exactly what they always have."
|
|
6970
|
+
},
|
|
6895
6971
|
{
|
|
6896
6972
|
"kind": "method",
|
|
6897
6973
|
"name": "applyAgent",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TurnFailureReason } from '@genesislcap/foundation-ai';
|
|
1
2
|
/**
|
|
2
3
|
* Event map for the AI activity bus.
|
|
3
4
|
*
|
|
@@ -61,8 +62,16 @@ export interface AgenticActivityEvents {
|
|
|
61
62
|
* spinner animation lifecycle (which can flap mid-turn as tool calls resolve), whereas
|
|
62
63
|
* this event is a one-shot per turn boundary. Use this for hooks tied to a turn ending
|
|
63
64
|
* (telemetry flushes, post-turn workspace transitions, deferred state recomputation).
|
|
65
|
+
*
|
|
66
|
+
* The detail carries the turn's outcome (PTC-0): on a failure it is `{ failureReason }`
|
|
67
|
+
* with the typed `TurnFailureReason`; on a clean completion, a user cancel, a
|
|
68
|
+
* driver dispose, and an agent handoff the detail is `undefined` — the historical shape,
|
|
69
|
+
* kept byte-identical so subscribers that only care about the boundary can keep ignoring
|
|
70
|
+
* it. Structured-cloneable so it survives the cross-tab BroadcastChannel hop.
|
|
64
71
|
*/
|
|
65
|
-
'tool-loop-end':
|
|
72
|
+
'tool-loop-end': {
|
|
73
|
+
failureReason?: TurnFailureReason;
|
|
74
|
+
} | undefined;
|
|
66
75
|
/**
|
|
67
76
|
* Fired when a tool handler hands a widget to the user mid-loop and parks awaiting it
|
|
68
77
|
* (`requestInteraction`) — the turn is suspended between provider calls, with no request
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-activity-channel.d.ts","sourceRoot":"","sources":["../../../src/channel/ai-activity-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,qBAAqB;IACpC,2FAA2F;IAC3F,YAAY,EAAE;QAAE,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACtC,iFAAiF;IACjF,WAAW,EAAE,SAAS,CAAC;IACvB,mJAAmJ;IACnJ,aAAa,EAAE,SAAS,CAAC;IACzB,wJAAwJ;IACxJ,YAAY,EAAE,SAAS,CAAC;IACxB;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,EAAE,SAAS,CAAC;IAC7B
|
|
1
|
+
{"version":3,"file":"ai-activity-channel.d.ts","sourceRoot":"","sources":["../../../src/channel/ai-activity-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,qBAAqB;IACpC,2FAA2F;IAC3F,YAAY,EAAE;QAAE,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACtC,iFAAiF;IACjF,WAAW,EAAE,SAAS,CAAC;IACvB,mJAAmJ;IACnJ,aAAa,EAAE,SAAS,CAAC;IACzB,wJAAwJ;IACxJ,YAAY,EAAE,SAAS,CAAC;IACxB;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,EAAE,SAAS,CAAC;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,eAAe,EAAE;QAAE,aAAa,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,SAAS,CAAC;IACnE;;;;;;;;;;;OAWG;IACH,uBAAuB,EAAE,SAAS,CAAC;IACnC;;;;;OAKG;IACH,sBAAsB,EAAE,SAAS,CAAC;CACnC"}
|
|
@@ -398,6 +398,23 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
|
|
|
398
398
|
* cached history would otherwise gain a stray marker on remount).
|
|
399
399
|
*/
|
|
400
400
|
private completeAbortedTurn;
|
|
401
|
+
/**
|
|
402
|
+
* Build the `done` loop result, carrying the typed failure reason when the turn
|
|
403
|
+
* bailed (PTC-0). The discriminant stays `'done'` either way — the same value a
|
|
404
|
+
* clean turn returns — so consumers matching on `reason === 'done'` are unchanged;
|
|
405
|
+
* `failureReason` is simply present on a failure and absent on success. Omitted
|
|
406
|
+
* (rather than set to `undefined`) so a happy-path result stays byte-identical to
|
|
407
|
+
* the historical `{ reason: 'done' }`.
|
|
408
|
+
*/
|
|
409
|
+
private turnDone;
|
|
410
|
+
/** The typed failure reason on a loop result, or `undefined` for a clean turn / handoff. */
|
|
411
|
+
private static failureReasonOf;
|
|
412
|
+
/**
|
|
413
|
+
* Build the `tool-loop-end` event detail for a turn's result. A failure carries a
|
|
414
|
+
* `{ failureReason }` detail; a clean turn emits `undefined` — the historical shape,
|
|
415
|
+
* kept byte-identical so subscribers see exactly what they always have.
|
|
416
|
+
*/
|
|
417
|
+
private static loopEndDetail;
|
|
401
418
|
/**
|
|
402
419
|
* Swap in a new agent's configuration. Called by OrchestratingDriver before
|
|
403
420
|
* each specialist turn so the shared driver runs with the right tools and prompt.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-driver.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-driver/chat-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAGlB,cAAc,EACd,gBAAgB,EAChB,WAAW,EAGX,cAAc,EAKd,yBAAyB,EAEzB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"chat-driver.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-driver/chat-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAGlB,cAAc,EACd,gBAAgB,EAChB,WAAW,EAGX,cAAc,EAKd,yBAAyB,EAEzB,qBAAqB,EAGtB,MAAM,4BAA4B,CAAC;AAOpC,OAAO,KAAK,EACV,WAAW,EAIX,iBAAiB,EAIjB,oBAAoB,EACpB,iBAAiB,EAElB,MAAM,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAU1E,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AA8CxE,wFAAwF;AACxF,eAAO,MAAM,yBAAyB,yBAAyB,CAAC;AAMhE;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;AAE9E;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qGAAqG;IACrG,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gGAAgG;IAChG,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAQD;;;;;;;;;GASG;AACH,qBAAa,UAAW,SAAQ,WAAY,YAAW,QAAQ;IA0S3D,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAKjC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAGlC,oFAAoF;IACpF,OAAO,CAAC,QAAQ,CAAC,UAAU;IAlT7B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,IAAI,CAAS;IACrB,kFAAkF;IAClF,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,mBAAmB,CAUvB;IACJ;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB,CAA+C;IAE1E,OAAO,CAAC,YAAY,CAAC,CAAoB;IACzC;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAAuB;IAC9C;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAE2B;IAC1D;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY,CAAmB;IACvC;;;;OAIG;IACH,OAAO,CAAC,mBAAmB,CAAC,CAEsB;IAClD,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,eAAe,CAAC,CAAS;IACjC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAClC,OAAO,CAAC,WAAW,CAAC,CAAoB;IACxC;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB,CAAC,CAIjB;IAChB;;;OAGG;IACH,OAAO,CAAC,wBAAwB,CAAC,CAA4C;IAC7E;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA+C;IAChF;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY,CAAK;IACzB;;;;;OAKG;IACH,OAAO,CAAC,OAAO,CAAK;IACpB;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB,CAAK;IAC9B;;;;;;;OAOG;IACH,OAAO,CAAC,iBAAiB,CAAK;IAE9B,8EAA8E;IAC9E,OAAO,CAAC,SAAS,CAAwB;IACzC,8FAA8F;IAC9F,OAAO,CAAC,kBAAkB,CAAK;IAC/B,6FAA6F;IAC7F,OAAO,CAAC,2BAA2B,CAAK;IACxC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAqB;IAC5D;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;IAC1D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,qEAAqE;IACrE,OAAO,CAAC,YAAY,CAAuC;IAC3D;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB,CAAkC;IAC5D;;;;;;OAMG;IACH,OAAO,CAAC,UAAU,CAAS;IAC3B;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAAgD;IACvE;;;;;;;;OAQG;IACH,OAAO,CAAC,qBAAqB,CAAS;IACtC;;;;OAIG;IACH,OAAO,CAAC,aAAa,CAAsB;IAC3C,+FAA+F;IAC/F,OAAO,CAAC,eAAe,CAAK;IAC5B,4EAA4E;IAC5E,OAAO,CAAC,gBAAgB,CAAC,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAyB;IAE7D;;;;;;OAMG;IACH,OAAO,CAAC,cAAc,CAAyB;IAC/C,0GAA0G;IAC1G,OAAO,CAAC,aAAa,CAAS;IAC9B,8EAA8E;IAC9E,OAAO,CAAC,uBAAuB,CAAC,CAAa;IAE7C;;;OAGG;IACH,OAAO,CAAC,mBAAmB,CAAC,CAAgB;IAC5C;;;OAGG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAAmB;IAClD;;;;OAIG;IACH,OAAO,CAAC,qBAAqB,CAAC,CAAkB;IAChD;;;OAGG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAAmB;IAClD;;;OAGG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAAmB;IAClD;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB,CAAC,CAAsB;IACrD;;;;OAIG;IACH,OAAO,CAAC,qBAAqB,CAAiC;IAC9D,iFAAiF;IACjF,OAAO,CAAC,wBAAwB,CAAC,CAAS;IAC1C,wFAAwF;IACxF,OAAO,CAAC,0BAA0B,CAAC,CAAS;IAC5C;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC;;;;OAIG;IACH,OAAO,CAAC,oBAAoB,CAAC,CAAiB;IAC9C;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB,CAAoE;IAC/F;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB,CAAC,CAAa;gBAGtB,gBAAgB,EAAE,kBAAkB,EACrD,YAAY,GAAE,iBAAsB,EACpC,eAAe,GAAE,oBAAyB,EAC1C,YAAY,CAAC,EAAE,iBAAiB,EAChC,aAAa,CAAC,EAAE,WAAW,EAAE,EACZ,iBAAiB,GAAE,MAAoC,EACxE,iBAAiB,GAAE,MAAoC,EACvD,gBAAgB,GAAE,MAAmC;IACrD,oFAAoF;IACnE,UAAU,GAAE,MAAW;IA0C1C;;;;;;;;;;;;;;OAcG;IACH,OAAO,IAAI,IAAI;IAef;;;;;OAKG;IACH,MAAM,IAAI,IAAI;IAMd;;;OAGG;IACH,OAAO,CAAC,SAAS;IAcjB,oGAAoG;IACpG,OAAO,CAAC,OAAO;IAKf;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAU3B;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ;IAIhB,4FAA4F;IAC5F,OAAO,CAAC,MAAM,CAAC,eAAe;IAI9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IAO5B;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IA0DrC;;;;;;;OAOG;YACW,4BAA4B;IAwB1C;;;OAGG;IACH,qBAAqB,IAAI,MAAM;IAI/B;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;;;OAKG;YACW,sBAAsB;IA+BpC;;;;;OAKG;YACW,wBAAwB;IAkBtC;;;;OAIG;YACW,gBAAgB;IAU9B;;;OAGG;IACH,qBAAqB,IAAI;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS;IAIxD;;;;OAIG;IACH,cAAc,IAAI,IAAI;IAItB;;;;OAIG;IACH,kBAAkB,IAAI;QAAE,MAAM,EAAE,qBAAqB,CAAA;KAAE,GAAG,SAAS;IAInE;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAKpB;;;OAGG;IACH,wBAAwB,IAAI,OAAO;IAInC;;;;;;OAMG;IACH,gBAAgB,IAAI,aAAa,CAAC,YAAY,CAAC;IAI/C;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,wBAAwB;IA2BhC;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAoC1B;;;OAGG;IACH,2BAA2B,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,GAAG,IAAI;IAIxF,UAAU,IAAI,aAAa,CAAC,WAAW,CAAC;IAIxC,aAAa,IAAI,SAAS,WAAW,EAAE;IAIvC,0DAA0D;IAC1D,kBAAkB,IAAI,MAAM,EAAE;IAIxB,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;IAiHpB;;;;OAIG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;;;;;OAQG;IACG,OAAO,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAmE5C,MAAM,IAAI,OAAO;IAIjB;;;;;OAKG;IACI,2BAA2B,CAChC,EAAE,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,yBAAyB,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3F,IAAI;IAIP;;;;;;;;;;;;;;;;;;OAkBG;IACU,kBAAkB,CAAC,CAAC,EAC/B,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,GAAG,EACT,OAAO,CAAC,EAAE,yBAAyB,GAClC,OAAO,CAAC,CAAC,CAAC;IAmEb;;;OAGG;IACI,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;IA2DnE;;;;OAIG;IACI,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAInF;;;;;;;OAOG;IACH,OAAO,CAAC,yBAAyB;IASjC;;;OAGG;IACI,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI;IAS3C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAmD/F;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,mBAAmB;IA8F3B;;;;;OAKG;YACW,cAAc;IAoN5B;;;OAGG;IACG,mBAAmB,CAAC,eAAe,CAAC,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiDrF,wFAAwF;IACxF,OAAO,CAAC,OAAO;IAKf;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;;;;;;;;OASG;IACH,OAAO,CAAC,cAAc;IAMtB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA+BxB,uFAAuF;IACvF,OAAO,CAAC,QAAQ;IAsChB,OAAO,CAAC,aAAa;IAQrB,8EAA8E;IAC9E,OAAO,CAAC,SAAS;IAWjB,mFAAmF;IACnF,OAAO,CAAC,2BAA2B;YAkCrB,WAAW;IAyyBzB,OAAO,CAAC,eAAe;CAoBxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-driver.test.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-driver/chat-driver.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat-driver.test.d.ts","sourceRoot":"","sources":["../../../../src/components/chat-driver/chat-driver.test.ts"],"names":[],"mappings":"AA4BA,OAAO,uBAAuB,CAAC"}
|
package/dist/dts/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from './state/persistence';
|
|
|
16
16
|
export * from './provider/ai-provider-switcher';
|
|
17
17
|
export * from './provider/assistant-app-settings';
|
|
18
18
|
export * from './utils/tool-fold';
|
|
19
|
+
export type { TurnFailureReason } from './state/debug-event-log';
|
|
19
20
|
export type { TimelineMessage } from './utils/flatten-sub-agent-messages';
|
|
20
21
|
export type { CostSessionModelEntry, CostSessionRecord } from './utils/cost-session-history';
|
|
21
22
|
export type { ModelTagAppearance } from '@genesislcap/foundation-ai';
|
package/dist/dts/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,YAAY,EAAE,+BAA+B,EAAE,MAAM,gEAAgE,CAAC;AACtH,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC;AAClD,cAAc,mBAAmB,CAAC;AAClC,YAAY,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAC7F,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,YAAY,EAAE,+BAA+B,EAAE,MAAM,gEAAgE,CAAC;AACtH,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC;AAClD,cAAc,mBAAmB,CAAC;AAClC,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,YAAY,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAC7F,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC"}
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*
|
|
24
24
|
* @internal
|
|
25
25
|
*/
|
|
26
|
+
import type { TurnFailureReason } from '@genesislcap/foundation-ai';
|
|
26
27
|
/**
|
|
27
28
|
* Catalogue of meta event names. This is the documented surface — extend it as
|
|
28
29
|
* new events are wired in (Tier 2/3 lifecycle, interaction, provider events).
|
|
@@ -68,19 +69,14 @@ export interface MetaEvent {
|
|
|
68
69
|
export declare function recordMetaEvent(key: string, type: MetaEventType, detail?: Record<string, unknown>): void;
|
|
69
70
|
/**
|
|
70
71
|
* Why a turn failed or was retried — stamped as `detail.reason` on `turn.error`
|
|
71
|
-
* and `turn.retry` events
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* - `unknown-tool-limit` — the model repeatedly called tools it couldn't dispatch,
|
|
78
|
-
* whether hallucinated or stale (real earlier, retired now).
|
|
79
|
-
* - `max-iterations` — the tool loop hit its iteration cap.
|
|
80
|
-
* - `response-truncated` — a turn stopped at the provider's output-token cap with an
|
|
81
|
-
* incomplete tool call; deterministic, so it bails without retry.
|
|
72
|
+
* and `turn.retry` events, and (since PTC-0) surfaced at the driver's loop
|
|
73
|
+
* boundary via {@link ChatDriverResult.failureReason}. Re-exported from
|
|
74
|
+
* `@genesislcap/foundation-ai` — where it lives alongside `ChatDriverResult` so
|
|
75
|
+
* the boundary field and this log surface can never drift apart — under its
|
|
76
|
+
* historical name so existing importers are unaffected. Enumerated so the set
|
|
77
|
+
* stays in sync with the README and call sites can't drift to ad-hoc strings.
|
|
82
78
|
*/
|
|
83
|
-
export type TurnFailureReason
|
|
79
|
+
export type { TurnFailureReason };
|
|
84
80
|
/**
|
|
85
81
|
* Record a turn-ending failure (`turn.error`, importance `high`). The `reason`
|
|
86
82
|
* is typed so the high-importance triage surface stays consistent; pass any
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug-event-log.d.ts","sourceRoot":"","sources":["../../../src/state/debug-event-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;GAGG;AACH,MAAM,MAAM,aAAa,GAErB,qBAAqB,GACrB,wBAAwB,GACxB,kBAAkB,GAClB,iBAAiB,GAEjB,gBAAgB,GAChB,cAAc,GACd,gBAAgB,GAEhB,eAAe,GACf,YAAY,GACZ,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,iBAAiB,GACjB,kBAAkB,GAClB,oBAAoB,GACpB,iBAAiB,GAEjB,eAAe,GACf,cAAc,GACd,gBAAgB,GAChB,mBAAmB,GAEnB,uBAAuB,GACvB,sBAAsB,GAEtB,iBAAiB,GACjB,2BAA2B,GAC3B,mBAAmB,GACnB,mBAAmB,GAEnB,eAAe,GACf,kBAAkB,GAClB,kBAAkB,GAClB,oBAAoB,GAEpB,iBAAiB,GACjB,kBAAkB,GAClB,yBAAyB,GACzB,qBAAqB,GACrB,mBAAmB,GACnB,wBAAwB,GACxB,2BAA2B,GAC3B,sBAAsB,GACtB,4BAA4B,CAAC;AAEjC;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE5D;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,aAAa,EAAE,mBAAmB,CA2C5E,CAAC;AAEF,4CAA4C;AAC5C,MAAM,WAAW,SAAS;IACxB,0FAA0F;IAC1F,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,IAAI,EAAE,aAAa,CAAC;IACpB,6EAA6E;IAC7E,UAAU,EAAE,mBAAmB,CAAC;IAChC,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAmBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,aAAa,EACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,IAAI,CAyBN;AAED
|
|
1
|
+
{"version":3,"file":"debug-event-log.d.ts","sourceRoot":"","sources":["../../../src/state/debug-event-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEpE;;;GAGG;AACH,MAAM,MAAM,aAAa,GAErB,qBAAqB,GACrB,wBAAwB,GACxB,kBAAkB,GAClB,iBAAiB,GAEjB,gBAAgB,GAChB,cAAc,GACd,gBAAgB,GAEhB,eAAe,GACf,YAAY,GACZ,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,iBAAiB,GACjB,kBAAkB,GAClB,oBAAoB,GACpB,iBAAiB,GAEjB,eAAe,GACf,cAAc,GACd,gBAAgB,GAChB,mBAAmB,GAEnB,uBAAuB,GACvB,sBAAsB,GAEtB,iBAAiB,GACjB,2BAA2B,GAC3B,mBAAmB,GACnB,mBAAmB,GAEnB,eAAe,GACf,kBAAkB,GAClB,kBAAkB,GAClB,oBAAoB,GAEpB,iBAAiB,GACjB,kBAAkB,GAClB,yBAAyB,GACzB,qBAAqB,GACrB,mBAAmB,GACnB,wBAAwB,GACxB,2BAA2B,GAC3B,sBAAsB,GACtB,4BAA4B,CAAC;AAEjC;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE5D;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,aAAa,EAAE,mBAAmB,CA2C5E,CAAC;AAEF,4CAA4C;AAC5C,MAAM,WAAW,SAAS;IACxB,0FAA0F;IAC1F,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,IAAI,EAAE,aAAa,CAAC;IACpB,6EAA6E;IAC7E,UAAU,EAAE,mBAAmB,CAAC;IAChC,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAmBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,aAAa,EACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,IAAI,CAyBN;AAED;;;;;;;;GAQG;AACH,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAElC;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,EACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,IAAI,CAEN;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,EACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,IAAI,CAEN;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,CAEnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CA0BrF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAc7C,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|
|
@@ -337,6 +337,30 @@ export class ChatDriver extends EventTarget {
|
|
|
337
337
|
}
|
|
338
338
|
return { reason: 'done' };
|
|
339
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Build the `done` loop result, carrying the typed failure reason when the turn
|
|
342
|
+
* bailed (PTC-0). The discriminant stays `'done'` either way — the same value a
|
|
343
|
+
* clean turn returns — so consumers matching on `reason === 'done'` are unchanged;
|
|
344
|
+
* `failureReason` is simply present on a failure and absent on success. Omitted
|
|
345
|
+
* (rather than set to `undefined`) so a happy-path result stays byte-identical to
|
|
346
|
+
* the historical `{ reason: 'done' }`.
|
|
347
|
+
*/
|
|
348
|
+
turnDone(failureReason) {
|
|
349
|
+
return failureReason ? { reason: 'done', failureReason } : { reason: 'done' };
|
|
350
|
+
}
|
|
351
|
+
/** The typed failure reason on a loop result, or `undefined` for a clean turn / handoff. */
|
|
352
|
+
static failureReasonOf(result) {
|
|
353
|
+
return result.reason === 'done' ? result.failureReason : undefined;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Build the `tool-loop-end` event detail for a turn's result. A failure carries a
|
|
357
|
+
* `{ failureReason }` detail; a clean turn emits `undefined` — the historical shape,
|
|
358
|
+
* kept byte-identical so subscribers see exactly what they always have.
|
|
359
|
+
*/
|
|
360
|
+
static loopEndDetail(result) {
|
|
361
|
+
const failureReason = ChatDriver.failureReasonOf(result);
|
|
362
|
+
return failureReason ? { failureReason } : undefined;
|
|
363
|
+
}
|
|
340
364
|
/**
|
|
341
365
|
* Swap in a new agent's configuration. Called by OrchestratingDriver before
|
|
342
366
|
* each specialist turn so the shared driver runs with the right tools and prompt.
|
|
@@ -1062,8 +1086,11 @@ export class ChatDriver extends EventTarget {
|
|
|
1062
1086
|
agent: this.activeAgentName,
|
|
1063
1087
|
});
|
|
1064
1088
|
agenticActivityBus.publish('tool-loop-start', undefined);
|
|
1089
|
+
// Captured so the `finally` can carry the turn's outcome onto `tool-loop-end`.
|
|
1090
|
+
let result = { reason: 'done' };
|
|
1065
1091
|
try {
|
|
1066
|
-
|
|
1092
|
+
result = yield this.runToolLoop(userInput, attachments);
|
|
1093
|
+
return result;
|
|
1067
1094
|
}
|
|
1068
1095
|
catch (e) {
|
|
1069
1096
|
logger.error('ChatDriver error:', e);
|
|
@@ -1078,7 +1105,8 @@ export class ChatDriver extends EventTarget {
|
|
|
1078
1105
|
role: 'assistant',
|
|
1079
1106
|
content: 'Sorry, something went wrong on my end. Please try again in a moment.',
|
|
1080
1107
|
});
|
|
1081
|
-
|
|
1108
|
+
result = this.turnDone('exception');
|
|
1109
|
+
return result;
|
|
1082
1110
|
}
|
|
1083
1111
|
finally {
|
|
1084
1112
|
recordMetaEvent(this.sessionKey, 'turn.end', {
|
|
@@ -1088,7 +1116,7 @@ export class ChatDriver extends EventTarget {
|
|
|
1088
1116
|
});
|
|
1089
1117
|
this.busy = false;
|
|
1090
1118
|
this.endTurn();
|
|
1091
|
-
agenticActivityBus.publish('tool-loop-end',
|
|
1119
|
+
agenticActivityBus.publish('tool-loop-end', ChatDriver.loopEndDetail(result));
|
|
1092
1120
|
}
|
|
1093
1121
|
});
|
|
1094
1122
|
}
|
|
@@ -1373,8 +1401,11 @@ export class ChatDriver extends EventTarget {
|
|
|
1373
1401
|
agent: this.activeAgentName,
|
|
1374
1402
|
});
|
|
1375
1403
|
agenticActivityBus.publish('tool-loop-start', undefined);
|
|
1404
|
+
// Captured so the `finally` can carry the turn's outcome onto `tool-loop-end`.
|
|
1405
|
+
let result = { reason: 'done' };
|
|
1376
1406
|
try {
|
|
1377
|
-
|
|
1407
|
+
result = yield this.runToolLoop('', undefined, transientPrimer);
|
|
1408
|
+
return result;
|
|
1378
1409
|
}
|
|
1379
1410
|
catch (e) {
|
|
1380
1411
|
logger.error('ChatDriver error:', e);
|
|
@@ -1389,7 +1420,8 @@ export class ChatDriver extends EventTarget {
|
|
|
1389
1420
|
role: 'assistant',
|
|
1390
1421
|
content: 'Sorry, something went wrong on my end. Please try again in a moment.',
|
|
1391
1422
|
});
|
|
1392
|
-
|
|
1423
|
+
result = this.turnDone('exception');
|
|
1424
|
+
return result;
|
|
1393
1425
|
}
|
|
1394
1426
|
finally {
|
|
1395
1427
|
recordMetaEvent(this.sessionKey, 'turn.end', {
|
|
@@ -1399,7 +1431,7 @@ export class ChatDriver extends EventTarget {
|
|
|
1399
1431
|
});
|
|
1400
1432
|
this.busy = false;
|
|
1401
1433
|
this.endTurn();
|
|
1402
|
-
agenticActivityBus.publish('tool-loop-end',
|
|
1434
|
+
agenticActivityBus.publish('tool-loop-end', ChatDriver.loopEndDetail(result));
|
|
1403
1435
|
}
|
|
1404
1436
|
});
|
|
1405
1437
|
}
|
|
@@ -1784,7 +1816,7 @@ export class ChatDriver extends EventTarget {
|
|
|
1784
1816
|
content: 'While working on your request, I repeatedly called my tools incorrectly. This often works on a second try — would you like me to try again? If it happens again, try breaking your request into smaller steps.',
|
|
1785
1817
|
});
|
|
1786
1818
|
}
|
|
1787
|
-
return
|
|
1819
|
+
return this.turnDone('malformed-function-call');
|
|
1788
1820
|
}
|
|
1789
1821
|
// The response was truncated at the provider's output-token cap while it
|
|
1790
1822
|
// still carried a tool call — its arguments are incomplete and unusable.
|
|
@@ -1813,7 +1845,7 @@ export class ChatDriver extends EventTarget {
|
|
|
1813
1845
|
content: 'My response was cut off because a single step reached the model output limit. This usually means one step tried to produce too much at once — try breaking your request into smaller steps.',
|
|
1814
1846
|
});
|
|
1815
1847
|
}
|
|
1816
|
-
return
|
|
1848
|
+
return this.turnDone('response-truncated');
|
|
1817
1849
|
}
|
|
1818
1850
|
// A request timeout from the transport (tagged `TimeoutError`) is not a
|
|
1819
1851
|
// bug on our end — surface it distinctly instead of letting it fall
|
|
@@ -1839,7 +1871,9 @@ export class ChatDriver extends EventTarget {
|
|
|
1839
1871
|
content: 'The request timed out. You can ask me to try again, or break this into a smaller step.',
|
|
1840
1872
|
});
|
|
1841
1873
|
}
|
|
1842
|
-
|
|
1874
|
+
// Recorded as `exception` above (there is no separate `timeout` member of
|
|
1875
|
+
// TurnFailureReason for the main turn); surface the same reason here.
|
|
1876
|
+
return this.turnDone('exception');
|
|
1843
1877
|
}
|
|
1844
1878
|
// The request was aborted: either a user cancel (turnController) or a
|
|
1845
1879
|
// driver dispose (lifecycleController, chained into the turn). A user
|
|
@@ -1899,7 +1933,7 @@ export class ChatDriver extends EventTarget {
|
|
|
1899
1933
|
content: 'While working on your request, I repeatedly generated a blank response. This often works on a second try — would you like me to try again? If it happens again, try breaking your request into smaller steps.',
|
|
1900
1934
|
});
|
|
1901
1935
|
}
|
|
1902
|
-
return
|
|
1936
|
+
return this.turnDone('empty-response');
|
|
1903
1937
|
}
|
|
1904
1938
|
else {
|
|
1905
1939
|
// Split one model response into separate, individually-toggleable messages so each has its
|
|
@@ -2213,7 +2247,7 @@ export class ChatDriver extends EventTarget {
|
|
|
2213
2247
|
content: "I'm sorry, I repeatedly tried to use tools that aren't available to me, so I couldn't complete that. If a 'Download agent log' option appears in the Settings (cog) menu, you can download the log and share it with whoever set up this assistant to help fix the issue.",
|
|
2214
2248
|
});
|
|
2215
2249
|
}
|
|
2216
|
-
return
|
|
2250
|
+
return this.turnDone('unknown-tool-limit');
|
|
2217
2251
|
}
|
|
2218
2252
|
const firstContinuation = systemCalls[0];
|
|
2219
2253
|
if (firstContinuation) {
|
|
@@ -2223,9 +2257,12 @@ export class ChatDriver extends EventTarget {
|
|
|
2223
2257
|
// Sub-agent early exit — checked here so the exit point mirrors the
|
|
2224
2258
|
// system-call pattern above. Set by completeSubAgent() in a tool handler.
|
|
2225
2259
|
if (this.subAgentCompletion) {
|
|
2226
|
-
return
|
|
2260
|
+
return this.turnDone();
|
|
2227
2261
|
}
|
|
2228
2262
|
}
|
|
2263
|
+
// The loop fell through: either it hit the iteration cap (a failure) or it
|
|
2264
|
+
// broke on a clean final answer (success). Only the former carries a reason.
|
|
2265
|
+
let failureReason;
|
|
2229
2266
|
if (iterations >= this.maxToolIterations) {
|
|
2230
2267
|
logger.warn('ChatDriver: reached max tool iterations, stopping');
|
|
2231
2268
|
recordTurnError(this.sessionKey, 'max-iterations', {
|
|
@@ -2244,8 +2281,9 @@ export class ChatDriver extends EventTarget {
|
|
|
2244
2281
|
content: "I've reached my limit for this response. You can ask me to continue and I'll pick up where I left off.",
|
|
2245
2282
|
});
|
|
2246
2283
|
}
|
|
2284
|
+
failureReason = 'max-iterations';
|
|
2247
2285
|
}
|
|
2248
|
-
return
|
|
2286
|
+
return this.turnDone(failureReason);
|
|
2249
2287
|
});
|
|
2250
2288
|
}
|
|
2251
2289
|
appendToHistory(message) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
|
-
import { isChatToolCallUnknown } from '@genesislcap/foundation-ai';
|
|
2
|
+
import { isChatToolCallUnknown, MalformedFunctionCallError, ResponseTruncatedError, } from '@genesislcap/foundation-ai';
|
|
3
3
|
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
4
4
|
import { agenticActivityBus } from '../../channel/ai-activity-bus';
|
|
5
5
|
import { clearMetaEventRegistry, getMetaEvents } from '../../state/debug-event-log';
|
|
@@ -1621,3 +1621,135 @@ condense('phaseEnd: collapses at endPhase() while the agent keeps running', () =
|
|
|
1621
1621
|
assert.is((_a = events[0].detail) === null || _a === void 0 ? void 0 : _a.trigger, 'phaseEnd');
|
|
1622
1622
|
}));
|
|
1623
1623
|
condense.run();
|
|
1624
|
+
// ---------------------------------------------------------------------------
|
|
1625
|
+
// turn-outcome surfacing (PTC-0) — the typed failure reason must reach BOTH
|
|
1626
|
+
// the loop-boundary result (`ChatDriverResult.failureReason`) and the
|
|
1627
|
+
// `tool-loop-end` activity-bus event detail. Historically every exit flattened
|
|
1628
|
+
// to `{ reason: 'done' }` and the bus detail was `undefined`; these lock the
|
|
1629
|
+
// two seams together, one per `TurnFailureReason`, plus a happy-path compat
|
|
1630
|
+
// check that the legacy shape is byte-unchanged.
|
|
1631
|
+
// ---------------------------------------------------------------------------
|
|
1632
|
+
const outcome = createLogicSuite('ChatDriver turn-outcome surfacing');
|
|
1633
|
+
outcome.after(() => {
|
|
1634
|
+
agenticActivityBus.close();
|
|
1635
|
+
});
|
|
1636
|
+
/**
|
|
1637
|
+
* Subscribe to `tool-loop-end` and expose the most recent detail seen. `fired()` reports
|
|
1638
|
+
* whether the event was seen at all — distinct from `detail()`, which is `undefined` both
|
|
1639
|
+
* when no event fired and when a clean turn fired with the historical `undefined` detail.
|
|
1640
|
+
*/
|
|
1641
|
+
const captureLoopEnd = () => {
|
|
1642
|
+
let last;
|
|
1643
|
+
let seen = false;
|
|
1644
|
+
const stop = agenticActivityBus.subscribe('tool-loop-end', (d) => {
|
|
1645
|
+
last = d;
|
|
1646
|
+
seen = true;
|
|
1647
|
+
});
|
|
1648
|
+
return { detail: () => (seen ? last : undefined), fired: () => seen, stop };
|
|
1649
|
+
};
|
|
1650
|
+
/** A ChatDriver with an explicit (small) tool-iteration cap. */
|
|
1651
|
+
const makeCappedDriver = (config, provider, maxIterations, sessionKey = '') => {
|
|
1652
|
+
const driver = new ChatDriver(makeRegistry(provider), {}, [], undefined, undefined, maxIterations, 5, undefined, sessionKey);
|
|
1653
|
+
driver.applyAgent(config);
|
|
1654
|
+
return driver;
|
|
1655
|
+
};
|
|
1656
|
+
/**
|
|
1657
|
+
* Assert that a turn driven by `provider` surfaces `expected` at BOTH seams.
|
|
1658
|
+
* `driverFor` lets the max-iterations case swap in a low cap.
|
|
1659
|
+
*/
|
|
1660
|
+
const assertSurfacesReason = (label_1, provider_1, expected_1, ...args_1) => __awaiter(void 0, [label_1, provider_1, expected_1, ...args_1], void 0, function* (label, provider, expected, driverFor = (c, p, k) => makeDriver(c, p, k)) {
|
|
1661
|
+
var _a;
|
|
1662
|
+
clearMetaEventRegistry();
|
|
1663
|
+
const sessionKey = `outcome-${label}`;
|
|
1664
|
+
const config = agent({
|
|
1665
|
+
name: 'Static',
|
|
1666
|
+
toolDefinitions: [def('noop')],
|
|
1667
|
+
toolHandlers: { noop: () => __awaiter(void 0, void 0, void 0, function* () { return 'ok'; }) },
|
|
1668
|
+
});
|
|
1669
|
+
const driver = driverFor(config, provider, sessionKey);
|
|
1670
|
+
const cap = captureLoopEnd();
|
|
1671
|
+
const result = yield driver.sendMessage('go');
|
|
1672
|
+
// Seam 1 — the loop-boundary result. Discriminant stays 'done' (compat).
|
|
1673
|
+
assert.is(result.reason, 'done', `[${label}] discriminant stays 'done'`);
|
|
1674
|
+
assert.is(result.reason === 'done' ? result.failureReason : undefined, expected, `[${label}] result.failureReason surfaces the typed reason`);
|
|
1675
|
+
// Seam 2 — the activity-bus tool-loop-end detail.
|
|
1676
|
+
assert.ok(cap.detail(), `[${label}] a tool-loop-end event fired`);
|
|
1677
|
+
assert.is(cap.detail().failureReason, expected, `[${label}] the bus detail carries the same reason`);
|
|
1678
|
+
// Consistency: the debug-log turn.error records the same taxonomy.
|
|
1679
|
+
const err = getMetaEvents(sessionKey).find((e) => e.type === 'turn.error');
|
|
1680
|
+
assert.ok(err, `[${label}] a turn.error is recorded`);
|
|
1681
|
+
assert.is((_a = err.detail) === null || _a === void 0 ? void 0 : _a.reason, expected, `[${label}] the debug-log reason matches`);
|
|
1682
|
+
cap.stop();
|
|
1683
|
+
});
|
|
1684
|
+
/** A provider that returns a MALFORMED_FUNCTION_CALL on every call. */
|
|
1685
|
+
const malformedProvider = () => ({
|
|
1686
|
+
chat: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1687
|
+
throw new MalformedFunctionCallError('bad call');
|
|
1688
|
+
}),
|
|
1689
|
+
});
|
|
1690
|
+
/** A provider that returns an empty response on every call. */
|
|
1691
|
+
const emptyProvider = () => ({
|
|
1692
|
+
chat: () => __awaiter(void 0, void 0, void 0, function* () { return ({ role: 'assistant', content: '' }); }),
|
|
1693
|
+
});
|
|
1694
|
+
/** A provider that throws a ResponseTruncatedError (deterministic, no retry). */
|
|
1695
|
+
const truncatedProvider = () => ({
|
|
1696
|
+
chat: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1697
|
+
throw new ResponseTruncatedError('test-model', 1024, 1024, ['noop']);
|
|
1698
|
+
}),
|
|
1699
|
+
});
|
|
1700
|
+
/** A provider that throws a generic error (the sendMessage catch-all → 'exception'). */
|
|
1701
|
+
const throwingProvider = () => ({
|
|
1702
|
+
chat: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1703
|
+
throw new Error('boom');
|
|
1704
|
+
}),
|
|
1705
|
+
});
|
|
1706
|
+
/** A provider that never stops calling a valid tool (drives the iteration cap). */
|
|
1707
|
+
const neverStopsProvider = () => {
|
|
1708
|
+
let n = 0;
|
|
1709
|
+
return {
|
|
1710
|
+
chat: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1711
|
+
const id = `noop-${n}`;
|
|
1712
|
+
n += 1;
|
|
1713
|
+
return { role: 'assistant', content: '', toolCalls: [{ id, name: 'noop', args: {} }] };
|
|
1714
|
+
}),
|
|
1715
|
+
};
|
|
1716
|
+
};
|
|
1717
|
+
/** A provider that keeps calling a tool with no handler (hallucinated → limit). */
|
|
1718
|
+
const hallucinatedProvider = () => scriptedProvider(Array.from({ length: 6 }, (_u, i) => callsTool('ghost', `ghost-${i}`)));
|
|
1719
|
+
outcome('malformed-function-call surfaces at both seams', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1720
|
+
yield assertSurfacesReason('malformed', malformedProvider(), 'malformed-function-call');
|
|
1721
|
+
}));
|
|
1722
|
+
outcome('empty-response surfaces at both seams', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1723
|
+
yield assertSurfacesReason('empty', emptyProvider(), 'empty-response');
|
|
1724
|
+
}));
|
|
1725
|
+
outcome('response-truncated surfaces at both seams', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1726
|
+
yield assertSurfacesReason('truncated', truncatedProvider(), 'response-truncated');
|
|
1727
|
+
}));
|
|
1728
|
+
outcome('exception surfaces at both seams', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1729
|
+
yield assertSurfacesReason('exception', throwingProvider(), 'exception');
|
|
1730
|
+
}));
|
|
1731
|
+
outcome('unknown-tool-limit surfaces at both seams', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1732
|
+
yield assertSurfacesReason('unknown-tool', hallucinatedProvider(), 'unknown-tool-limit');
|
|
1733
|
+
}));
|
|
1734
|
+
outcome('max-iterations surfaces at both seams', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1735
|
+
yield assertSurfacesReason('max-iter', neverStopsProvider(), 'max-iterations', (c, p, k) => makeCappedDriver(c, p, 2, k));
|
|
1736
|
+
}));
|
|
1737
|
+
outcome('a clean turn leaves the legacy shape byte-unchanged (no failureReason)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1738
|
+
clearMetaEventRegistry();
|
|
1739
|
+
const config = agent({ name: 'Static' });
|
|
1740
|
+
// Plain-text reply ends the turn cleanly on the first call.
|
|
1741
|
+
const driver = makeDriver(config, scriptedProvider([{ role: 'assistant', content: 'hi' }]), 'outcome-ok');
|
|
1742
|
+
const cap = captureLoopEnd();
|
|
1743
|
+
const result = yield driver.sendMessage('go');
|
|
1744
|
+
// The result is exactly `{ reason: 'done' }` — no `failureReason` key added.
|
|
1745
|
+
assert.equal(result, { reason: 'done' }, 'happy-path result is the historical shape');
|
|
1746
|
+
assert.not.ok('failureReason' in result, 'no failureReason key is present on a clean turn');
|
|
1747
|
+
// The bus event still fires, and its detail is the historical `undefined` — a clean
|
|
1748
|
+
// turn emits no detail object at all (byte-shape compat), not `{ failureReason: undefined }`.
|
|
1749
|
+
assert.ok(cap.fired(), 'a tool-loop-end event fired');
|
|
1750
|
+
assert.is(cap.detail(), undefined, 'a clean turn emits the historical undefined detail');
|
|
1751
|
+
// And no turn.error was recorded.
|
|
1752
|
+
assert.not.ok(getMetaEvents('outcome-ok').some((e) => e.type === 'turn.error'), 'a clean turn records no turn.error');
|
|
1753
|
+
cap.stop();
|
|
1754
|
+
}));
|
|
1755
|
+
outcome.run();
|
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.
|
|
4
|
+
"version": "14.495.0",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/ai-assistant.d.ts",
|
|
@@ -64,25 +64,25 @@
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@genesislcap/foundation-testing": "14.
|
|
68
|
-
"@genesislcap/genx": "14.
|
|
69
|
-
"@genesislcap/rollup-builder": "14.
|
|
70
|
-
"@genesislcap/ts-builder": "14.
|
|
71
|
-
"@genesislcap/uvu-playwright-builder": "14.
|
|
72
|
-
"@genesislcap/vite-builder": "14.
|
|
73
|
-
"@genesislcap/webpack-builder": "14.
|
|
67
|
+
"@genesislcap/foundation-testing": "14.495.0",
|
|
68
|
+
"@genesislcap/genx": "14.495.0",
|
|
69
|
+
"@genesislcap/rollup-builder": "14.495.0",
|
|
70
|
+
"@genesislcap/ts-builder": "14.495.0",
|
|
71
|
+
"@genesislcap/uvu-playwright-builder": "14.495.0",
|
|
72
|
+
"@genesislcap/vite-builder": "14.495.0",
|
|
73
|
+
"@genesislcap/webpack-builder": "14.495.0",
|
|
74
74
|
"@types/dompurify": "^3.0.5",
|
|
75
75
|
"@types/marked": "^5.0.2"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@genesislcap/foundation-ai": "14.
|
|
79
|
-
"@genesislcap/foundation-logger": "14.
|
|
80
|
-
"@genesislcap/foundation-notifications": "14.
|
|
81
|
-
"@genesislcap/foundation-redux": "14.
|
|
82
|
-
"@genesislcap/foundation-ui": "14.
|
|
83
|
-
"@genesislcap/foundation-utils": "14.
|
|
84
|
-
"@genesislcap/rapid-design-system": "14.
|
|
85
|
-
"@genesislcap/web-core": "14.
|
|
78
|
+
"@genesislcap/foundation-ai": "14.495.0",
|
|
79
|
+
"@genesislcap/foundation-logger": "14.495.0",
|
|
80
|
+
"@genesislcap/foundation-notifications": "14.495.0",
|
|
81
|
+
"@genesislcap/foundation-redux": "14.495.0",
|
|
82
|
+
"@genesislcap/foundation-ui": "14.495.0",
|
|
83
|
+
"@genesislcap/foundation-utils": "14.495.0",
|
|
84
|
+
"@genesislcap/rapid-design-system": "14.495.0",
|
|
85
|
+
"@genesislcap/web-core": "14.495.0",
|
|
86
86
|
"dompurify": "^3.3.1",
|
|
87
87
|
"marked": "^17.0.3"
|
|
88
88
|
},
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
"access": "public"
|
|
96
96
|
},
|
|
97
97
|
"customElements": "dist/custom-elements.json",
|
|
98
|
-
"gitHead": "
|
|
98
|
+
"gitHead": "24d26a75271717d40c0b6168b983bd8536a3db7f"
|
|
99
99
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { TurnFailureReason } from '@genesislcap/foundation-ai';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Event map for the AI activity bus.
|
|
3
5
|
*
|
|
@@ -59,8 +61,14 @@ export interface AgenticActivityEvents {
|
|
|
59
61
|
* spinner animation lifecycle (which can flap mid-turn as tool calls resolve), whereas
|
|
60
62
|
* this event is a one-shot per turn boundary. Use this for hooks tied to a turn ending
|
|
61
63
|
* (telemetry flushes, post-turn workspace transitions, deferred state recomputation).
|
|
64
|
+
*
|
|
65
|
+
* The detail carries the turn's outcome (PTC-0): on a failure it is `{ failureReason }`
|
|
66
|
+
* with the typed `TurnFailureReason`; on a clean completion, a user cancel, a
|
|
67
|
+
* driver dispose, and an agent handoff the detail is `undefined` — the historical shape,
|
|
68
|
+
* kept byte-identical so subscribers that only care about the boundary can keep ignoring
|
|
69
|
+
* it. Structured-cloneable so it survives the cross-tab BroadcastChannel hop.
|
|
62
70
|
*/
|
|
63
|
-
'tool-loop-end': undefined;
|
|
71
|
+
'tool-loop-end': { failureReason?: TurnFailureReason } | undefined;
|
|
64
72
|
/**
|
|
65
73
|
* Fired when a tool handler hands a widget to the user mid-loop and parks awaiting it
|
|
66
74
|
* (`requestInteraction`) — the turn is suspended between provider calls, with no request
|
|
@@ -2,14 +2,20 @@ import type {
|
|
|
2
2
|
AIProvider,
|
|
3
3
|
AIProviderRegistry,
|
|
4
4
|
CachePolicy,
|
|
5
|
+
ChatDriverResult,
|
|
5
6
|
ChatMessage,
|
|
6
7
|
ChatRequestOptions,
|
|
7
8
|
ChatToolCall,
|
|
8
9
|
ChatToolChoice,
|
|
9
10
|
ChatToolDefinition,
|
|
10
11
|
InteractionResult,
|
|
12
|
+
TurnFailureReason,
|
|
13
|
+
} from '@genesislcap/foundation-ai';
|
|
14
|
+
import {
|
|
15
|
+
isChatToolCallUnknown,
|
|
16
|
+
MalformedFunctionCallError,
|
|
17
|
+
ResponseTruncatedError,
|
|
11
18
|
} from '@genesislcap/foundation-ai';
|
|
12
|
-
import { isChatToolCallUnknown } from '@genesislcap/foundation-ai';
|
|
13
19
|
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
14
20
|
import { agenticActivityBus } from '../../channel/ai-activity-bus';
|
|
15
21
|
import type { AgentConfig } from '../../config/config';
|
|
@@ -2175,3 +2181,210 @@ condense('phaseEnd: collapses at endPhase() while the agent keeps running', asyn
|
|
|
2175
2181
|
});
|
|
2176
2182
|
|
|
2177
2183
|
condense.run();
|
|
2184
|
+
|
|
2185
|
+
// ---------------------------------------------------------------------------
|
|
2186
|
+
// turn-outcome surfacing (PTC-0) — the typed failure reason must reach BOTH
|
|
2187
|
+
// the loop-boundary result (`ChatDriverResult.failureReason`) and the
|
|
2188
|
+
// `tool-loop-end` activity-bus event detail. Historically every exit flattened
|
|
2189
|
+
// to `{ reason: 'done' }` and the bus detail was `undefined`; these lock the
|
|
2190
|
+
// two seams together, one per `TurnFailureReason`, plus a happy-path compat
|
|
2191
|
+
// check that the legacy shape is byte-unchanged.
|
|
2192
|
+
// ---------------------------------------------------------------------------
|
|
2193
|
+
|
|
2194
|
+
const outcome = createLogicSuite('ChatDriver turn-outcome surfacing');
|
|
2195
|
+
|
|
2196
|
+
outcome.after(() => {
|
|
2197
|
+
agenticActivityBus.close();
|
|
2198
|
+
});
|
|
2199
|
+
|
|
2200
|
+
/**
|
|
2201
|
+
* Subscribe to `tool-loop-end` and expose the most recent detail seen. `fired()` reports
|
|
2202
|
+
* whether the event was seen at all — distinct from `detail()`, which is `undefined` both
|
|
2203
|
+
* when no event fired and when a clean turn fired with the historical `undefined` detail.
|
|
2204
|
+
*/
|
|
2205
|
+
const captureLoopEnd = (): {
|
|
2206
|
+
detail: () => { failureReason?: TurnFailureReason } | undefined;
|
|
2207
|
+
fired: () => boolean;
|
|
2208
|
+
stop: () => void;
|
|
2209
|
+
} => {
|
|
2210
|
+
let last: { failureReason?: TurnFailureReason } | undefined;
|
|
2211
|
+
let seen = false;
|
|
2212
|
+
const stop = agenticActivityBus.subscribe('tool-loop-end', (d) => {
|
|
2213
|
+
last = d;
|
|
2214
|
+
seen = true;
|
|
2215
|
+
});
|
|
2216
|
+
return { detail: () => (seen ? last : undefined), fired: () => seen, stop };
|
|
2217
|
+
};
|
|
2218
|
+
|
|
2219
|
+
/** A ChatDriver with an explicit (small) tool-iteration cap. */
|
|
2220
|
+
const makeCappedDriver = (
|
|
2221
|
+
config: AgentConfig,
|
|
2222
|
+
provider: AIProvider,
|
|
2223
|
+
maxIterations: number,
|
|
2224
|
+
sessionKey = '',
|
|
2225
|
+
): ChatDriver => {
|
|
2226
|
+
const driver = new ChatDriver(
|
|
2227
|
+
makeRegistry(provider),
|
|
2228
|
+
{},
|
|
2229
|
+
[],
|
|
2230
|
+
undefined,
|
|
2231
|
+
undefined,
|
|
2232
|
+
maxIterations,
|
|
2233
|
+
5,
|
|
2234
|
+
undefined,
|
|
2235
|
+
sessionKey,
|
|
2236
|
+
);
|
|
2237
|
+
driver.applyAgent(config);
|
|
2238
|
+
return driver;
|
|
2239
|
+
};
|
|
2240
|
+
|
|
2241
|
+
/**
|
|
2242
|
+
* Assert that a turn driven by `provider` surfaces `expected` at BOTH seams.
|
|
2243
|
+
* `driverFor` lets the max-iterations case swap in a low cap.
|
|
2244
|
+
*/
|
|
2245
|
+
const assertSurfacesReason = async (
|
|
2246
|
+
label: string,
|
|
2247
|
+
provider: AIProvider,
|
|
2248
|
+
expected: TurnFailureReason,
|
|
2249
|
+
driverFor: (config: AgentConfig, provider: AIProvider, sessionKey: string) => ChatDriver = (
|
|
2250
|
+
c,
|
|
2251
|
+
p,
|
|
2252
|
+
k,
|
|
2253
|
+
) => makeDriver(c, p, k),
|
|
2254
|
+
): Promise<void> => {
|
|
2255
|
+
clearMetaEventRegistry();
|
|
2256
|
+
const sessionKey = `outcome-${label}`;
|
|
2257
|
+
const config = agent({
|
|
2258
|
+
name: 'Static',
|
|
2259
|
+
toolDefinitions: [def('noop')],
|
|
2260
|
+
toolHandlers: { noop: async () => 'ok' },
|
|
2261
|
+
});
|
|
2262
|
+
const driver = driverFor(config, provider, sessionKey);
|
|
2263
|
+
const cap = captureLoopEnd();
|
|
2264
|
+
|
|
2265
|
+
const result: ChatDriverResult = await driver.sendMessage('go');
|
|
2266
|
+
|
|
2267
|
+
// Seam 1 — the loop-boundary result. Discriminant stays 'done' (compat).
|
|
2268
|
+
assert.is(result.reason, 'done', `[${label}] discriminant stays 'done'`);
|
|
2269
|
+
assert.is(
|
|
2270
|
+
result.reason === 'done' ? result.failureReason : undefined,
|
|
2271
|
+
expected,
|
|
2272
|
+
`[${label}] result.failureReason surfaces the typed reason`,
|
|
2273
|
+
);
|
|
2274
|
+
|
|
2275
|
+
// Seam 2 — the activity-bus tool-loop-end detail.
|
|
2276
|
+
assert.ok(cap.detail(), `[${label}] a tool-loop-end event fired`);
|
|
2277
|
+
assert.is(
|
|
2278
|
+
cap.detail()!.failureReason,
|
|
2279
|
+
expected,
|
|
2280
|
+
`[${label}] the bus detail carries the same reason`,
|
|
2281
|
+
);
|
|
2282
|
+
|
|
2283
|
+
// Consistency: the debug-log turn.error records the same taxonomy.
|
|
2284
|
+
const err = getMetaEvents(sessionKey).find((e) => e.type === 'turn.error');
|
|
2285
|
+
assert.ok(err, `[${label}] a turn.error is recorded`);
|
|
2286
|
+
assert.is(err!.detail?.reason, expected, `[${label}] the debug-log reason matches`);
|
|
2287
|
+
|
|
2288
|
+
cap.stop();
|
|
2289
|
+
};
|
|
2290
|
+
|
|
2291
|
+
/** A provider that returns a MALFORMED_FUNCTION_CALL on every call. */
|
|
2292
|
+
const malformedProvider = (): AIProvider => ({
|
|
2293
|
+
chat: async (): Promise<ChatMessage> => {
|
|
2294
|
+
throw new MalformedFunctionCallError('bad call');
|
|
2295
|
+
},
|
|
2296
|
+
});
|
|
2297
|
+
|
|
2298
|
+
/** A provider that returns an empty response on every call. */
|
|
2299
|
+
const emptyProvider = (): AIProvider => ({
|
|
2300
|
+
chat: async (): Promise<ChatMessage> => ({ role: 'assistant', content: '' }),
|
|
2301
|
+
});
|
|
2302
|
+
|
|
2303
|
+
/** A provider that throws a ResponseTruncatedError (deterministic, no retry). */
|
|
2304
|
+
const truncatedProvider = (): AIProvider => ({
|
|
2305
|
+
chat: async (): Promise<ChatMessage> => {
|
|
2306
|
+
throw new ResponseTruncatedError('test-model', 1024, 1024, ['noop']);
|
|
2307
|
+
},
|
|
2308
|
+
});
|
|
2309
|
+
|
|
2310
|
+
/** A provider that throws a generic error (the sendMessage catch-all → 'exception'). */
|
|
2311
|
+
const throwingProvider = (): AIProvider => ({
|
|
2312
|
+
chat: async (): Promise<ChatMessage> => {
|
|
2313
|
+
throw new Error('boom');
|
|
2314
|
+
},
|
|
2315
|
+
});
|
|
2316
|
+
|
|
2317
|
+
/** A provider that never stops calling a valid tool (drives the iteration cap). */
|
|
2318
|
+
const neverStopsProvider = (): AIProvider => {
|
|
2319
|
+
let n = 0;
|
|
2320
|
+
return {
|
|
2321
|
+
chat: async (): Promise<ChatMessage> => {
|
|
2322
|
+
const id = `noop-${n}`;
|
|
2323
|
+
n += 1;
|
|
2324
|
+
return { role: 'assistant', content: '', toolCalls: [{ id, name: 'noop', args: {} }] };
|
|
2325
|
+
},
|
|
2326
|
+
};
|
|
2327
|
+
};
|
|
2328
|
+
|
|
2329
|
+
/** A provider that keeps calling a tool with no handler (hallucinated → limit). */
|
|
2330
|
+
const hallucinatedProvider = (): AIProvider =>
|
|
2331
|
+
scriptedProvider(Array.from({ length: 6 }, (_u, i) => callsTool('ghost', `ghost-${i}`)));
|
|
2332
|
+
|
|
2333
|
+
outcome('malformed-function-call surfaces at both seams', async () => {
|
|
2334
|
+
await assertSurfacesReason('malformed', malformedProvider(), 'malformed-function-call');
|
|
2335
|
+
});
|
|
2336
|
+
|
|
2337
|
+
outcome('empty-response surfaces at both seams', async () => {
|
|
2338
|
+
await assertSurfacesReason('empty', emptyProvider(), 'empty-response');
|
|
2339
|
+
});
|
|
2340
|
+
|
|
2341
|
+
outcome('response-truncated surfaces at both seams', async () => {
|
|
2342
|
+
await assertSurfacesReason('truncated', truncatedProvider(), 'response-truncated');
|
|
2343
|
+
});
|
|
2344
|
+
|
|
2345
|
+
outcome('exception surfaces at both seams', async () => {
|
|
2346
|
+
await assertSurfacesReason('exception', throwingProvider(), 'exception');
|
|
2347
|
+
});
|
|
2348
|
+
|
|
2349
|
+
outcome('unknown-tool-limit surfaces at both seams', async () => {
|
|
2350
|
+
await assertSurfacesReason('unknown-tool', hallucinatedProvider(), 'unknown-tool-limit');
|
|
2351
|
+
});
|
|
2352
|
+
|
|
2353
|
+
outcome('max-iterations surfaces at both seams', async () => {
|
|
2354
|
+
await assertSurfacesReason('max-iter', neverStopsProvider(), 'max-iterations', (c, p, k) =>
|
|
2355
|
+
makeCappedDriver(c, p, 2, k),
|
|
2356
|
+
);
|
|
2357
|
+
});
|
|
2358
|
+
|
|
2359
|
+
outcome('a clean turn leaves the legacy shape byte-unchanged (no failureReason)', async () => {
|
|
2360
|
+
clearMetaEventRegistry();
|
|
2361
|
+
const config = agent({ name: 'Static' });
|
|
2362
|
+
// Plain-text reply ends the turn cleanly on the first call.
|
|
2363
|
+
const driver = makeDriver(
|
|
2364
|
+
config,
|
|
2365
|
+
scriptedProvider([{ role: 'assistant', content: 'hi' }]),
|
|
2366
|
+
'outcome-ok',
|
|
2367
|
+
);
|
|
2368
|
+
const cap = captureLoopEnd();
|
|
2369
|
+
|
|
2370
|
+
const result: ChatDriverResult = await driver.sendMessage('go');
|
|
2371
|
+
|
|
2372
|
+
// The result is exactly `{ reason: 'done' }` — no `failureReason` key added.
|
|
2373
|
+
assert.equal(result, { reason: 'done' }, 'happy-path result is the historical shape');
|
|
2374
|
+
assert.not.ok('failureReason' in result, 'no failureReason key is present on a clean turn');
|
|
2375
|
+
|
|
2376
|
+
// The bus event still fires, and its detail is the historical `undefined` — a clean
|
|
2377
|
+
// turn emits no detail object at all (byte-shape compat), not `{ failureReason: undefined }`.
|
|
2378
|
+
assert.ok(cap.fired(), 'a tool-loop-end event fired');
|
|
2379
|
+
assert.is(cap.detail(), undefined, 'a clean turn emits the historical undefined detail');
|
|
2380
|
+
|
|
2381
|
+
// And no turn.error was recorded.
|
|
2382
|
+
assert.not.ok(
|
|
2383
|
+
getMetaEvents('outcome-ok').some((e) => e.type === 'turn.error'),
|
|
2384
|
+
'a clean turn records no turn.error',
|
|
2385
|
+
);
|
|
2386
|
+
|
|
2387
|
+
cap.stop();
|
|
2388
|
+
});
|
|
2389
|
+
|
|
2390
|
+
outcome.run();
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
InteractionResult,
|
|
18
18
|
SubAgentFailureReason,
|
|
19
19
|
SubAgentRequestOptions,
|
|
20
|
+
TurnFailureReason,
|
|
20
21
|
} from '@genesislcap/foundation-ai';
|
|
21
22
|
import {
|
|
22
23
|
isObservableAIProviderRegistry,
|
|
@@ -631,6 +632,35 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
631
632
|
return { reason: 'done' };
|
|
632
633
|
}
|
|
633
634
|
|
|
635
|
+
/**
|
|
636
|
+
* Build the `done` loop result, carrying the typed failure reason when the turn
|
|
637
|
+
* bailed (PTC-0). The discriminant stays `'done'` either way — the same value a
|
|
638
|
+
* clean turn returns — so consumers matching on `reason === 'done'` are unchanged;
|
|
639
|
+
* `failureReason` is simply present on a failure and absent on success. Omitted
|
|
640
|
+
* (rather than set to `undefined`) so a happy-path result stays byte-identical to
|
|
641
|
+
* the historical `{ reason: 'done' }`.
|
|
642
|
+
*/
|
|
643
|
+
private turnDone(failureReason?: TurnFailureReason): ChatDriverResult {
|
|
644
|
+
return failureReason ? { reason: 'done', failureReason } : { reason: 'done' };
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/** The typed failure reason on a loop result, or `undefined` for a clean turn / handoff. */
|
|
648
|
+
private static failureReasonOf(result: ChatDriverResult): TurnFailureReason | undefined {
|
|
649
|
+
return result.reason === 'done' ? result.failureReason : undefined;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Build the `tool-loop-end` event detail for a turn's result. A failure carries a
|
|
654
|
+
* `{ failureReason }` detail; a clean turn emits `undefined` — the historical shape,
|
|
655
|
+
* kept byte-identical so subscribers see exactly what they always have.
|
|
656
|
+
*/
|
|
657
|
+
private static loopEndDetail(
|
|
658
|
+
result: ChatDriverResult,
|
|
659
|
+
): { failureReason: TurnFailureReason } | undefined {
|
|
660
|
+
const failureReason = ChatDriver.failureReasonOf(result);
|
|
661
|
+
return failureReason ? { failureReason } : undefined;
|
|
662
|
+
}
|
|
663
|
+
|
|
634
664
|
/**
|
|
635
665
|
* Swap in a new agent's configuration. Called by OrchestratingDriver before
|
|
636
666
|
* each specialist turn so the shared driver runs with the right tools and prompt.
|
|
@@ -1412,8 +1442,11 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1412
1442
|
});
|
|
1413
1443
|
agenticActivityBus.publish('tool-loop-start', undefined);
|
|
1414
1444
|
|
|
1445
|
+
// Captured so the `finally` can carry the turn's outcome onto `tool-loop-end`.
|
|
1446
|
+
let result: ChatDriverResult = { reason: 'done' };
|
|
1415
1447
|
try {
|
|
1416
|
-
|
|
1448
|
+
result = await this.runToolLoop(userInput, attachments);
|
|
1449
|
+
return result;
|
|
1417
1450
|
} catch (e) {
|
|
1418
1451
|
logger.error('ChatDriver error:', e);
|
|
1419
1452
|
recordTurnError(this.sessionKey, 'exception', {
|
|
@@ -1427,7 +1460,8 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1427
1460
|
role: 'assistant',
|
|
1428
1461
|
content: 'Sorry, something went wrong on my end. Please try again in a moment.',
|
|
1429
1462
|
});
|
|
1430
|
-
|
|
1463
|
+
result = this.turnDone('exception');
|
|
1464
|
+
return result;
|
|
1431
1465
|
} finally {
|
|
1432
1466
|
recordMetaEvent(this.sessionKey, 'turn.end', {
|
|
1433
1467
|
phase: 'sendMessage',
|
|
@@ -1436,7 +1470,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1436
1470
|
});
|
|
1437
1471
|
this.busy = false;
|
|
1438
1472
|
this.endTurn();
|
|
1439
|
-
agenticActivityBus.publish('tool-loop-end',
|
|
1473
|
+
agenticActivityBus.publish('tool-loop-end', ChatDriver.loopEndDetail(result));
|
|
1440
1474
|
}
|
|
1441
1475
|
}
|
|
1442
1476
|
|
|
@@ -1782,8 +1816,11 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1782
1816
|
agent: this.activeAgentName,
|
|
1783
1817
|
});
|
|
1784
1818
|
agenticActivityBus.publish('tool-loop-start', undefined);
|
|
1819
|
+
// Captured so the `finally` can carry the turn's outcome onto `tool-loop-end`.
|
|
1820
|
+
let result: ChatDriverResult = { reason: 'done' };
|
|
1785
1821
|
try {
|
|
1786
|
-
|
|
1822
|
+
result = await this.runToolLoop('', undefined, transientPrimer);
|
|
1823
|
+
return result;
|
|
1787
1824
|
} catch (e) {
|
|
1788
1825
|
logger.error('ChatDriver error:', e);
|
|
1789
1826
|
recordTurnError(this.sessionKey, 'exception', {
|
|
@@ -1797,7 +1834,8 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1797
1834
|
role: 'assistant',
|
|
1798
1835
|
content: 'Sorry, something went wrong on my end. Please try again in a moment.',
|
|
1799
1836
|
});
|
|
1800
|
-
|
|
1837
|
+
result = this.turnDone('exception');
|
|
1838
|
+
return result;
|
|
1801
1839
|
} finally {
|
|
1802
1840
|
recordMetaEvent(this.sessionKey, 'turn.end', {
|
|
1803
1841
|
phase: 'continueFromHistory',
|
|
@@ -1806,7 +1844,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1806
1844
|
});
|
|
1807
1845
|
this.busy = false;
|
|
1808
1846
|
this.endTurn();
|
|
1809
|
-
agenticActivityBus.publish('tool-loop-end',
|
|
1847
|
+
agenticActivityBus.publish('tool-loop-end', ChatDriver.loopEndDetail(result));
|
|
1810
1848
|
}
|
|
1811
1849
|
}
|
|
1812
1850
|
|
|
@@ -2244,7 +2282,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2244
2282
|
'While working on your request, I repeatedly called my tools incorrectly. This often works on a second try — would you like me to try again? If it happens again, try breaking your request into smaller steps.',
|
|
2245
2283
|
});
|
|
2246
2284
|
}
|
|
2247
|
-
return
|
|
2285
|
+
return this.turnDone('malformed-function-call');
|
|
2248
2286
|
}
|
|
2249
2287
|
// The response was truncated at the provider's output-token cap while it
|
|
2250
2288
|
// still carried a tool call — its arguments are incomplete and unusable.
|
|
@@ -2273,7 +2311,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2273
2311
|
'My response was cut off because a single step reached the model output limit. This usually means one step tried to produce too much at once — try breaking your request into smaller steps.',
|
|
2274
2312
|
});
|
|
2275
2313
|
}
|
|
2276
|
-
return
|
|
2314
|
+
return this.turnDone('response-truncated');
|
|
2277
2315
|
}
|
|
2278
2316
|
// A request timeout from the transport (tagged `TimeoutError`) is not a
|
|
2279
2317
|
// bug on our end — surface it distinctly instead of letting it fall
|
|
@@ -2299,7 +2337,9 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2299
2337
|
'The request timed out. You can ask me to try again, or break this into a smaller step.',
|
|
2300
2338
|
});
|
|
2301
2339
|
}
|
|
2302
|
-
|
|
2340
|
+
// Recorded as `exception` above (there is no separate `timeout` member of
|
|
2341
|
+
// TurnFailureReason for the main turn); surface the same reason here.
|
|
2342
|
+
return this.turnDone('exception');
|
|
2303
2343
|
}
|
|
2304
2344
|
// The request was aborted: either a user cancel (turnController) or a
|
|
2305
2345
|
// driver dispose (lifecycleController, chained into the turn). A user
|
|
@@ -2380,7 +2420,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2380
2420
|
'While working on your request, I repeatedly generated a blank response. This often works on a second try — would you like me to try again? If it happens again, try breaking your request into smaller steps.',
|
|
2381
2421
|
});
|
|
2382
2422
|
}
|
|
2383
|
-
return
|
|
2423
|
+
return this.turnDone('empty-response');
|
|
2384
2424
|
} else {
|
|
2385
2425
|
// Split one model response into separate, individually-toggleable messages so each has its
|
|
2386
2426
|
// own visibility toggle and debug-log category:
|
|
@@ -2744,7 +2784,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2744
2784
|
"I'm sorry, I repeatedly tried to use tools that aren't available to me, so I couldn't complete that. If a 'Download agent log' option appears in the Settings (cog) menu, you can download the log and share it with whoever set up this assistant to help fix the issue.",
|
|
2745
2785
|
});
|
|
2746
2786
|
}
|
|
2747
|
-
return
|
|
2787
|
+
return this.turnDone('unknown-tool-limit');
|
|
2748
2788
|
}
|
|
2749
2789
|
|
|
2750
2790
|
const firstContinuation = systemCalls[0];
|
|
@@ -2759,10 +2799,13 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2759
2799
|
// Sub-agent early exit — checked here so the exit point mirrors the
|
|
2760
2800
|
// system-call pattern above. Set by completeSubAgent() in a tool handler.
|
|
2761
2801
|
if (this.subAgentCompletion) {
|
|
2762
|
-
return
|
|
2802
|
+
return this.turnDone();
|
|
2763
2803
|
}
|
|
2764
2804
|
}
|
|
2765
2805
|
|
|
2806
|
+
// The loop fell through: either it hit the iteration cap (a failure) or it
|
|
2807
|
+
// broke on a clean final answer (success). Only the former carries a reason.
|
|
2808
|
+
let failureReason: TurnFailureReason | undefined;
|
|
2766
2809
|
if (iterations >= this.maxToolIterations) {
|
|
2767
2810
|
logger.warn('ChatDriver: reached max tool iterations, stopping');
|
|
2768
2811
|
recordTurnError(this.sessionKey, 'max-iterations', {
|
|
@@ -2781,9 +2824,10 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2781
2824
|
"I've reached my limit for this response. You can ask me to continue and I'll pick up where I left off.",
|
|
2782
2825
|
});
|
|
2783
2826
|
}
|
|
2827
|
+
failureReason = 'max-iterations';
|
|
2784
2828
|
}
|
|
2785
2829
|
|
|
2786
|
-
return
|
|
2830
|
+
return this.turnDone(failureReason);
|
|
2787
2831
|
}
|
|
2788
2832
|
|
|
2789
2833
|
private appendToHistory(message: ChatMessage): void {
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from './state/persistence';
|
|
|
16
16
|
export * from './provider/ai-provider-switcher';
|
|
17
17
|
export * from './provider/assistant-app-settings';
|
|
18
18
|
export * from './utils/tool-fold';
|
|
19
|
+
export type { TurnFailureReason } from './state/debug-event-log';
|
|
19
20
|
export type { TimelineMessage } from './utils/flatten-sub-agent-messages';
|
|
20
21
|
export type { CostSessionModelEntry, CostSessionRecord } from './utils/cost-session-history';
|
|
21
22
|
export type { ModelTagAppearance } from '@genesislcap/foundation-ai';
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
* @internal
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
+
import type { TurnFailureReason } from '@genesislcap/foundation-ai';
|
|
28
|
+
|
|
27
29
|
/**
|
|
28
30
|
* Catalogue of meta event names. This is the documented surface — extend it as
|
|
29
31
|
* new events are wired in (Tier 2/3 lifecycle, interaction, provider events).
|
|
@@ -211,25 +213,14 @@ export function recordMetaEvent(
|
|
|
211
213
|
|
|
212
214
|
/**
|
|
213
215
|
* Why a turn failed or was retried — stamped as `detail.reason` on `turn.error`
|
|
214
|
-
* and `turn.retry` events
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* - `unknown-tool-limit` — the model repeatedly called tools it couldn't dispatch,
|
|
221
|
-
* whether hallucinated or stale (real earlier, retired now).
|
|
222
|
-
* - `max-iterations` — the tool loop hit its iteration cap.
|
|
223
|
-
* - `response-truncated` — a turn stopped at the provider's output-token cap with an
|
|
224
|
-
* incomplete tool call; deterministic, so it bails without retry.
|
|
216
|
+
* and `turn.retry` events, and (since PTC-0) surfaced at the driver's loop
|
|
217
|
+
* boundary via {@link ChatDriverResult.failureReason}. Re-exported from
|
|
218
|
+
* `@genesislcap/foundation-ai` — where it lives alongside `ChatDriverResult` so
|
|
219
|
+
* the boundary field and this log surface can never drift apart — under its
|
|
220
|
+
* historical name so existing importers are unaffected. Enumerated so the set
|
|
221
|
+
* stays in sync with the README and call sites can't drift to ad-hoc strings.
|
|
225
222
|
*/
|
|
226
|
-
export type TurnFailureReason
|
|
227
|
-
| 'exception'
|
|
228
|
-
| 'malformed-function-call'
|
|
229
|
-
| 'empty-response'
|
|
230
|
-
| 'unknown-tool-limit'
|
|
231
|
-
| 'max-iterations'
|
|
232
|
-
| 'response-truncated';
|
|
223
|
+
export type { TurnFailureReason };
|
|
233
224
|
|
|
234
225
|
/**
|
|
235
226
|
* Record a turn-ending failure (`turn.error`, importance `high`). The `reason`
|