@genesislcap/ai-assistant 15.19.0 → 15.19.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/package.json +17 -18
- package/api-extractor.json +0 -11
- package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.invocation-scope.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.invocation-scope.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.thinking-policy.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.thinking-policy.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.trace-capture.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.trace-capture.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.turn-usage.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.turn-usage.test.d.ts.map +0 -1
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.test.d.ts +0 -2
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.test.d.ts.map +0 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts +0 -2
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts.map +0 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts +0 -2
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts.map +0 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts +0 -2
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts.map +0 -1
- package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts +0 -2
- package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts.map +0 -1
- package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts +0 -2
- package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts.map +0 -1
- package/dist/dts/config/define-stateful-agent.test.d.ts +0 -2
- package/dist/dts/config/define-stateful-agent.test.d.ts.map +0 -1
- package/dist/dts/config/validate-providers.test.d.ts +0 -2
- package/dist/dts/config/validate-providers.test.d.ts.map +0 -1
- package/dist/dts/main/blocked-state.test.d.ts +0 -2
- package/dist/dts/main/blocked-state.test.d.ts.map +0 -1
- package/dist/dts/main/budget-meter.test.d.ts +0 -2
- package/dist/dts/main/budget-meter.test.d.ts.map +0 -1
- package/dist/dts/main/cost-session-banking.test.d.ts +0 -2
- package/dist/dts/main/cost-session-banking.test.d.ts.map +0 -1
- package/dist/dts/main/file-attachments.test.d.ts +0 -2
- package/dist/dts/main/file-attachments.test.d.ts.map +0 -1
- package/dist/dts/main/interaction-cost.test.d.ts +0 -2
- package/dist/dts/main/interaction-cost.test.d.ts.map +0 -1
- package/dist/dts/main/main.styles.test.d.ts +0 -2
- package/dist/dts/main/main.styles.test.d.ts.map +0 -1
- package/dist/dts/main/persistence-broken-sources.test.d.ts +0 -2
- package/dist/dts/main/persistence-broken-sources.test.d.ts.map +0 -1
- package/dist/dts/main/popout-interaction-gate.test.d.ts +0 -2
- package/dist/dts/main/popout-interaction-gate.test.d.ts.map +0 -1
- package/dist/dts/state/ai-assistant-slice.test.d.ts +0 -2
- package/dist/dts/state/ai-assistant-slice.test.d.ts.map +0 -1
- package/dist/dts/state/debug-event-log.test.d.ts +0 -2
- package/dist/dts/state/debug-event-log.test.d.ts.map +0 -1
- package/dist/dts/state/driver-registry.test.d.ts +0 -2
- package/dist/dts/state/driver-registry.test.d.ts.map +0 -1
- package/dist/dts/state/interaction-context.test.d.ts +0 -2
- package/dist/dts/state/interaction-context.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts +0 -2
- package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/diagnostics.test.d.ts +0 -2
- package/dist/dts/state/persistence/diagnostics.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/session-persistence-provider.test.d.ts +0 -2
- package/dist/dts/state/persistence/session-persistence-provider.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/session-persistence.integration.test.d.ts +0 -2
- package/dist/dts/state/persistence/session-persistence.integration.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/session-persister.test.d.ts +0 -2
- package/dist/dts/state/persistence/session-persister.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/session-snapshot.test.d.ts +0 -2
- package/dist/dts/state/persistence/session-snapshot.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts +0 -2
- package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts.map +0 -1
- package/dist/dts/utils/animation-exclusivity.test.d.ts +0 -2
- package/dist/dts/utils/animation-exclusivity.test.d.ts.map +0 -1
- package/dist/dts/utils/collect-session-models.test.d.ts +0 -2
- package/dist/dts/utils/collect-session-models.test.d.ts.map +0 -1
- package/dist/dts/utils/condense-history.test.d.ts +0 -2
- package/dist/dts/utils/condense-history.test.d.ts.map +0 -1
- package/dist/dts/utils/cost-session-history.test.d.ts +0 -2
- package/dist/dts/utils/cost-session-history.test.d.ts.map +0 -1
- package/dist/dts/utils/derive-cost-session-title.test.d.ts +0 -2
- package/dist/dts/utils/derive-cost-session-title.test.d.ts.map +0 -1
- package/dist/dts/utils/flatten-sub-agent-messages.test.d.ts +0 -2
- package/dist/dts/utils/flatten-sub-agent-messages.test.d.ts.map +0 -1
- package/dist/dts/utils/history-transform.test.d.ts +0 -2
- package/dist/dts/utils/history-transform.test.d.ts.map +0 -1
- package/dist/dts/utils/message-partition.test.d.ts +0 -2
- package/dist/dts/utils/message-partition.test.d.ts.map +0 -1
- package/dist/dts/utils/resolve-cost-history-config.test.d.ts +0 -2
- package/dist/dts/utils/resolve-cost-history-config.test.d.ts.map +0 -1
- package/dist/dts/utils/resolve-preference-baseline.test.d.ts +0 -2
- package/dist/dts/utils/resolve-preference-baseline.test.d.ts.map +0 -1
- package/dist/dts/utils/strip-agent-handlers.test.d.ts +0 -2
- package/dist/dts/utils/strip-agent-handlers.test.d.ts.map +0 -1
- package/dist/dts/utils/sum-costs.test.d.ts +0 -2
- package/dist/dts/utils/sum-costs.test.d.ts.map +0 -1
- package/dist/dts/utils/sum-tokens.test.d.ts +0 -2
- package/dist/dts/utils/sum-tokens.test.d.ts.map +0 -1
- package/dist/dts/utils/sum-usage.test.d.ts +0 -2
- package/dist/dts/utils/sum-usage.test.d.ts.map +0 -1
- package/dist/dts/utils/usage-rows.test.d.ts +0 -2
- package/dist/dts/utils/usage-rows.test.d.ts.map +0 -1
- package/dist/esm/components/chat-driver/chat-driver.compact.test.js +0 -93
- package/dist/esm/components/chat-driver/chat-driver.invocation-scope.test.js +0 -238
- package/dist/esm/components/chat-driver/chat-driver.test.js +0 -2770
- package/dist/esm/components/chat-driver/chat-driver.thinking-policy.test.js +0 -137
- package/dist/esm/components/chat-driver/chat-driver.trace-capture.test.js +0 -200
- package/dist/esm/components/chat-driver/chat-driver.turn-usage.test.js +0 -268
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.test.js +0 -104
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.budget.test.js +0 -312
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.cost.test.js +0 -76
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.pin.test.js +0 -61
- package/dist/esm/components/settings-modal/settings-modal.styles.test.js +0 -80
- package/dist/esm/components/settings-modal/settings-modal.template.test.js +0 -91
- package/dist/esm/config/define-stateful-agent.test.js +0 -53
- package/dist/esm/config/validate-providers.test.js +0 -100
- package/dist/esm/main/blocked-state.test.js +0 -1065
- package/dist/esm/main/budget-meter.test.js +0 -359
- package/dist/esm/main/cost-session-banking.test.js +0 -538
- package/dist/esm/main/file-attachments.test.js +0 -164
- package/dist/esm/main/interaction-cost.test.js +0 -107
- package/dist/esm/main/main.styles.test.js +0 -161
- package/dist/esm/main/persistence-broken-sources.test.js +0 -180
- package/dist/esm/main/popout-interaction-gate.test.js +0 -54
- package/dist/esm/state/ai-assistant-slice.test.js +0 -229
- package/dist/esm/state/debug-event-log.test.js +0 -117
- package/dist/esm/state/driver-registry.test.js +0 -55
- package/dist/esm/state/interaction-context.test.js +0 -78
- package/dist/esm/state/persistence/diagnostics-cursors.test.js +0 -30
- package/dist/esm/state/persistence/diagnostics.test.js +0 -280
- package/dist/esm/state/persistence/session-persistence-provider.test.js +0 -110
- package/dist/esm/state/persistence/session-persistence.integration.test.js +0 -113
- package/dist/esm/state/persistence/session-persister.test.js +0 -613
- package/dist/esm/state/persistence/session-snapshot.test.js +0 -256
- package/dist/esm/state/persistence/stateful-restore.e2e.test.js +0 -158
- package/dist/esm/utils/animation-exclusivity.test.js +0 -52
- package/dist/esm/utils/collect-session-models.test.js +0 -53
- package/dist/esm/utils/condense-history.test.js +0 -547
- package/dist/esm/utils/cost-session-history.test.js +0 -174
- package/dist/esm/utils/derive-cost-session-title.test.js +0 -37
- package/dist/esm/utils/flatten-sub-agent-messages.test.js +0 -139
- package/dist/esm/utils/history-transform.test.js +0 -158
- package/dist/esm/utils/message-partition.test.js +0 -90
- package/dist/esm/utils/resolve-cost-history-config.test.js +0 -32
- package/dist/esm/utils/resolve-preference-baseline.test.js +0 -69
- package/dist/esm/utils/strip-agent-handlers.test.js +0 -81
- package/dist/esm/utils/sum-costs.test.js +0 -114
- package/dist/esm/utils/sum-tokens.test.js +0 -51
- package/dist/esm/utils/sum-usage.test.js +0 -164
- package/dist/esm/utils/usage-rows.test.js +0 -189
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/license.txt +0 -46
- package/sandbox/tsconfig.json +0 -8
- package/src/channel/ai-activity-bus.ts +0 -209
- package/src/channel/ai-activity-channel.ts +0 -146
- package/src/chat-driver-node.ts +0 -159
- package/src/components/activity-halo/activity-halo.ts +0 -106
- package/src/components/agent-picker/agent-picker.constants.ts +0 -2
- package/src/components/agent-picker/agent-picker.styles.ts +0 -24
- package/src/components/agent-picker/agent-picker.template.ts +0 -54
- package/src/components/agent-picker/agent-picker.ts +0 -99
- package/src/components/agent-picker/index.ts +0 -1
- package/src/components/ai-driver/ai-driver.ts +0 -166
- package/src/components/ai-driver/index.ts +0 -1
- package/src/components/chat-bubble/chat-bubble.styles.ts +0 -194
- package/src/components/chat-bubble/chat-bubble.template.ts +0 -67
- package/src/components/chat-bubble/chat-bubble.ts +0 -436
- package/src/components/chat-bubble/index.ts +0 -1
- package/src/components/chat-driver/align-event-globals.ts +0 -23
- package/src/components/chat-driver/chat-driver.compact.test.ts +0 -131
- package/src/components/chat-driver/chat-driver.invocation-scope.test.ts +0 -285
- package/src/components/chat-driver/chat-driver.test.ts +0 -3679
- package/src/components/chat-driver/chat-driver.thinking-policy.test.ts +0 -185
- package/src/components/chat-driver/chat-driver.trace-capture.test.ts +0 -251
- package/src/components/chat-driver/chat-driver.ts +0 -3925
- package/src/components/chat-driver/chat-driver.turn-usage.test.ts +0 -362
- package/src/components/chat-driver/index.ts +0 -1
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts +0 -7
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts +0 -6
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts +0 -144
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +0 -243
- package/src/components/chat-interaction-wrapper/index.ts +0 -3
- package/src/components/chat-markdown/chat-markdown.ts +0 -89
- package/src/components/chat-markdown/index.ts +0 -1
- package/src/components/flowing-waves-indicator.ts +0 -286
- package/src/components/halo-overlay.ts +0 -184
- package/src/components/orchestrating-driver/index.ts +0 -1
- package/src/components/orchestrating-driver/orchestrating-driver.budget.test.ts +0 -438
- package/src/components/orchestrating-driver/orchestrating-driver.cost.test.ts +0 -107
- package/src/components/orchestrating-driver/orchestrating-driver.pin.test.ts +0 -96
- package/src/components/orchestrating-driver/orchestrating-driver.ts +0 -813
- package/src/components/plasma-orb-indicator.ts +0 -281
- package/src/components/popout-manager/index.ts +0 -1
- package/src/components/popout-manager/popout-manager.ts +0 -212
- package/src/components/settings-modal/index.ts +0 -2
- package/src/components/settings-modal/settings-modal.styles.test.ts +0 -94
- package/src/components/settings-modal/settings-modal.styles.ts +0 -853
- package/src/components/settings-modal/settings-modal.template.test.ts +0 -124
- package/src/components/settings-modal/settings-modal.template.ts +0 -943
- package/src/components/waves-indicator.ts +0 -241
- package/src/config/config.ts +0 -608
- package/src/config/define-stateful-agent.test.ts +0 -64
- package/src/config/define-stateful-agent.ts +0 -642
- package/src/config/fallback-agents.ts +0 -29
- package/src/config/index.ts +0 -2
- package/src/config/validate-providers.test.ts +0 -148
- package/src/config/validate-providers.ts +0 -58
- package/src/index.ts +0 -29
- package/src/main/blocked-state.test.ts +0 -1444
- package/src/main/budget-meter.test.ts +0 -477
- package/src/main/cost-session-banking.test.ts +0 -679
- package/src/main/file-attachments.test.ts +0 -215
- package/src/main/index.ts +0 -3
- package/src/main/interaction-cost.test.ts +0 -140
- package/src/main/main.styles.test.ts +0 -216
- package/src/main/main.styles.ts +0 -995
- package/src/main/main.template.ts +0 -1119
- package/src/main/main.ts +0 -5432
- package/src/main/main.types.ts +0 -225
- package/src/main/persistence-broken-sources.test.ts +0 -219
- package/src/main/popout-interaction-gate.test.ts +0 -87
- package/src/provider/ai-provider-switcher.ts +0 -40
- package/src/provider/assistant-app-settings.ts +0 -84
- package/src/state/ai-assistant-slice.test.ts +0 -336
- package/src/state/ai-assistant-slice.ts +0 -604
- package/src/state/debug-event-log.test.ts +0 -154
- package/src/state/debug-event-log.ts +0 -358
- package/src/state/driver-registry.test.ts +0 -70
- package/src/state/driver-registry.ts +0 -79
- package/src/state/interaction-context.test.ts +0 -90
- package/src/state/interaction-context.ts +0 -101
- package/src/state/persistence/build-timeline-entries.ts +0 -66
- package/src/state/persistence/diagnostics-cursors.test.ts +0 -53
- package/src/state/persistence/diagnostics-cursors.ts +0 -80
- package/src/state/persistence/diagnostics.test.ts +0 -346
- package/src/state/persistence/diagnostics.ts +0 -220
- package/src/state/persistence/index.ts +0 -10
- package/src/state/persistence/persister-registry.ts +0 -55
- package/src/state/persistence/session-persistence-provider.test.ts +0 -136
- package/src/state/persistence/session-persistence-provider.ts +0 -226
- package/src/state/persistence/session-persistence.integration.test.ts +0 -141
- package/src/state/persistence/session-persister.test.ts +0 -787
- package/src/state/persistence/session-persister.ts +0 -917
- package/src/state/persistence/session-snapshot.test.ts +0 -352
- package/src/state/persistence/session-snapshot.ts +0 -344
- package/src/state/persistence/stateful-restore.e2e.test.ts +0 -229
- package/src/state/session-store.ts +0 -56
- package/src/styles/ai-colours.ts +0 -30
- package/src/styles/menu.ts +0 -187
- package/src/styles/settings-section.ts +0 -40
- package/src/suggestions/chat-suggestions.ts +0 -186
- package/src/tags/index.ts +0 -1
- package/src/types/ai-chat-widget.ts +0 -67
- package/src/types/interaction-context.ts +0 -82
- package/src/utils/animated-panel-toggle.ts +0 -62
- package/src/utils/animation-exclusivity.test.ts +0 -72
- package/src/utils/animation-exclusivity.ts +0 -40
- package/src/utils/banked-usage-baselines.ts +0 -90
- package/src/utils/collect-session-models.test.ts +0 -82
- package/src/utils/collect-session-models.ts +0 -65
- package/src/utils/condense-history.test.ts +0 -746
- package/src/utils/condense-history.ts +0 -448
- package/src/utils/cost-session-history.test.ts +0 -226
- package/src/utils/cost-session-history.ts +0 -218
- package/src/utils/derive-cost-session-title.test.ts +0 -63
- package/src/utils/derive-cost-session-title.ts +0 -76
- package/src/utils/flatten-sub-agent-messages.test.ts +0 -163
- package/src/utils/flatten-sub-agent-messages.ts +0 -88
- package/src/utils/format-usd.ts +0 -26
- package/src/utils/history-transform.test.ts +0 -228
- package/src/utils/history-transform.ts +0 -163
- package/src/utils/index.ts +0 -3
- package/src/utils/logger.ts +0 -3
- package/src/utils/message-partition.test.ts +0 -129
- package/src/utils/message-partition.ts +0 -115
- package/src/utils/resolve-cost-history-config.test.ts +0 -38
- package/src/utils/resolve-cost-history-config.ts +0 -38
- package/src/utils/resolve-preference-baseline.test.ts +0 -84
- package/src/utils/resolve-preference-baseline.ts +0 -80
- package/src/utils/strip-agent-handlers.test.ts +0 -99
- package/src/utils/strip-agent-handlers.ts +0 -53
- package/src/utils/sum-costs.test.ts +0 -136
- package/src/utils/sum-costs.ts +0 -18
- package/src/utils/sum-tokens.test.ts +0 -93
- package/src/utils/sum-tokens.ts +0 -68
- package/src/utils/sum-usage.test.ts +0 -191
- package/src/utils/sum-usage.ts +0 -208
- package/src/utils/tool-fold.ts +0 -181
- package/src/utils/usage-rows.test.ts +0 -237
- package/src/utils/usage-rows.ts +0 -187
- package/src/utils/with-timeout.ts +0 -24
- package/tsconfig.json +0 -9
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
2
|
-
import { PERSISTED_SESSION_VERSION, createPersistedSession, isRestorableSession, reconcileAgentSnapshots, resolveNonResumableNotice, toLoadSessionPayload, keepThroughLastUserMessage, trimToRestorableBoundary, unknownAgentSnapshots, } from './session-snapshot';
|
|
3
|
-
const asMsg = (o) => o;
|
|
4
|
-
// GENC-1351 §5.1: the PersistedSession schema + its pure (de)serialize helpers.
|
|
5
|
-
// These have no DOM/storage deps, so they run straight in Node.
|
|
6
|
-
const Suite = createLogicSuite('persisted-session-schema');
|
|
7
|
-
const base = () => createPersistedSession({
|
|
8
|
-
sessionKey: 'k',
|
|
9
|
-
host: 'app.example',
|
|
10
|
-
messages: [],
|
|
11
|
-
pinnedAgentName: 'A',
|
|
12
|
-
flowOwnerAgentName: null,
|
|
13
|
-
sessionCostUsd: 2,
|
|
14
|
-
contextTokens: 10,
|
|
15
|
-
contextLimit: 100,
|
|
16
|
-
activeModel: 'm',
|
|
17
|
-
activeProviderName: 'p',
|
|
18
|
-
activeAgentName: 'A',
|
|
19
|
-
agentSnapshots: { A: { machine: { state: 'x', context: {} } } },
|
|
20
|
-
});
|
|
21
|
-
Suite('createPersistedSession stamps version + savedAt and keeps agentSnapshots', () => {
|
|
22
|
-
const s = base();
|
|
23
|
-
assert.is(s.version, PERSISTED_SESSION_VERSION);
|
|
24
|
-
assert.is(typeof s.savedAt, 'string');
|
|
25
|
-
assert.ok(s.savedAt.length > 0);
|
|
26
|
-
assert.equal(s.agentSnapshots, { A: { machine: { state: 'x', context: {} } } });
|
|
27
|
-
});
|
|
28
|
-
// GENC-1464 — the blocked latch is deliberately NOT persisted. The snapshot is
|
|
29
|
-
// per-stateKey and long-lived, so a persisted latch would survive an out-of-band
|
|
30
|
-
// budget raise with no in-element way to clear it: the user would return to a
|
|
31
|
-
// permanently dead composer against a budget that has headroom. Re-deriving the
|
|
32
|
-
// state (host pre-flight on mount, or the next 402) is strictly safer than
|
|
33
|
-
// remembering it. This test exists so a future "helpful" addition trips here and
|
|
34
|
-
// has to read that reasoning first — see `FoundationAiAssistant.blocked`'s
|
|
35
|
-
// Lifetime paragraph and docs/migration-GENC-1464.md §Scope.
|
|
36
|
-
Suite('the blocked latch is deliberately not persisted', () => {
|
|
37
|
-
const s = base();
|
|
38
|
-
assert.not.ok('blocked' in s, 'blocked must not enter the persisted snapshot');
|
|
39
|
-
assert.not.ok('blockedReason' in s, 'blockedReason must not enter the persisted snapshot');
|
|
40
|
-
// The per-vendor latches inherit the same lifetime, so they inherit the same
|
|
41
|
-
// rule — a persisted per-vendor wall is exactly as stale as a persisted global
|
|
42
|
-
// one, just harder to notice because only one vendor looks broken.
|
|
43
|
-
assert.not.ok('blockedVendors' in s, 'blockedVendors must not enter the persisted snapshot');
|
|
44
|
-
assert.not.ok('blockedVendorReasons' in s, 'blockedVendorReasons must not enter the persisted snapshot');
|
|
45
|
-
});
|
|
46
|
-
Suite('createPersistedSession defaults agentSnapshots to {}', () => {
|
|
47
|
-
const s = createPersistedSession({
|
|
48
|
-
sessionKey: 'k',
|
|
49
|
-
host: 'h',
|
|
50
|
-
messages: [],
|
|
51
|
-
pinnedAgentName: null,
|
|
52
|
-
flowOwnerAgentName: null,
|
|
53
|
-
sessionCostUsd: 0,
|
|
54
|
-
});
|
|
55
|
-
assert.equal(s.agentSnapshots, {});
|
|
56
|
-
});
|
|
57
|
-
Suite('isRestorableSession accepts a matching version + host', () => {
|
|
58
|
-
assert.ok(isRestorableSession(base(), { host: 'app.example' }));
|
|
59
|
-
});
|
|
60
|
-
Suite('isRestorableSession rejects version mismatch, cross-host, and malformed input', () => {
|
|
61
|
-
assert.ok(!isRestorableSession(Object.assign(Object.assign({}, base()), { version: 999 }), { host: 'app.example' }));
|
|
62
|
-
assert.ok(!isRestorableSession(base(), { host: 'other.example' }));
|
|
63
|
-
assert.ok(!isRestorableSession(null, { host: 'h' }));
|
|
64
|
-
assert.ok(!isRestorableSession({ version: PERSISTED_SESSION_VERSION, host: 'h' }, { host: 'h' }), 'missing messages array → not restorable');
|
|
65
|
-
});
|
|
66
|
-
Suite('toLoadSessionPayload projects only the redux-restorable fields', () => {
|
|
67
|
-
const p = toLoadSessionPayload(base());
|
|
68
|
-
assert.is(p.pinnedAgentName, 'A');
|
|
69
|
-
assert.is(p.flowOwnerAgentName, null);
|
|
70
|
-
// Cost is deliberately NOT projected — the slice re-derives it (and the token
|
|
71
|
-
// buckets) from `messages`, so a stored counter would be a second, divergeable
|
|
72
|
-
// source of truth.
|
|
73
|
-
assert.is(p.sessionCostUsd, undefined);
|
|
74
|
-
assert.is(p.contextTokens, 10);
|
|
75
|
-
assert.is(p.contextLimit, 100);
|
|
76
|
-
assert.is(p.activeModel, 'm');
|
|
77
|
-
assert.is(p.activeProviderName, 'p');
|
|
78
|
-
assert.equal(p.messages, []);
|
|
79
|
-
// agentSnapshots + activeAgentName are applied by the host, not via the slice.
|
|
80
|
-
assert.ok(!('agentSnapshots' in p));
|
|
81
|
-
assert.ok(!('activeAgentName' in p));
|
|
82
|
-
});
|
|
83
|
-
Suite('trimToRestorableBoundary returns a clean transcript unchanged (same ref)', () => {
|
|
84
|
-
const clean = [
|
|
85
|
-
asMsg({ role: 'user', content: 'hi' }),
|
|
86
|
-
asMsg({ role: 'assistant', content: 'hello' }),
|
|
87
|
-
];
|
|
88
|
-
assert.is(trimToRestorableBoundary(clean), clean);
|
|
89
|
-
assert.is(trimToRestorableBoundary([]).length, 0);
|
|
90
|
-
});
|
|
91
|
-
Suite('trimToRestorableBoundary drops a trailing unresolved interaction, keeps a resolved one', () => {
|
|
92
|
-
const user = asMsg({ role: 'user', content: 'hi' });
|
|
93
|
-
const pending = asMsg({
|
|
94
|
-
role: 'assistant',
|
|
95
|
-
content: '',
|
|
96
|
-
interaction: { interactionId: '2', componentName: 'x', data: {} },
|
|
97
|
-
});
|
|
98
|
-
const resolved = asMsg({
|
|
99
|
-
role: 'assistant',
|
|
100
|
-
content: '',
|
|
101
|
-
interaction: {
|
|
102
|
-
interactionId: '1',
|
|
103
|
-
componentName: 'x',
|
|
104
|
-
data: {},
|
|
105
|
-
resolved: { status: 'approved' },
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
assert.is(trimToRestorableBoundary([user, pending]).length, 1);
|
|
109
|
-
assert.is(trimToRestorableBoundary([user, resolved]).length, 2);
|
|
110
|
-
});
|
|
111
|
-
Suite('trimToRestorableBoundary drops dangling + partially-answered tool calls', () => {
|
|
112
|
-
const user = asMsg({ role: 'user', content: 'do x' });
|
|
113
|
-
const callAB = asMsg({
|
|
114
|
-
role: 'assistant',
|
|
115
|
-
content: '',
|
|
116
|
-
toolCalls: [
|
|
117
|
-
{ id: 'a', name: 'f', args: {} },
|
|
118
|
-
{ id: 'b', name: 'g', args: {} },
|
|
119
|
-
],
|
|
120
|
-
});
|
|
121
|
-
const resultA = asMsg({
|
|
122
|
-
role: 'tool',
|
|
123
|
-
content: '',
|
|
124
|
-
toolResult: { toolCallId: 'a', content: 'ra' },
|
|
125
|
-
});
|
|
126
|
-
const resultB = asMsg({
|
|
127
|
-
role: 'tool',
|
|
128
|
-
content: '',
|
|
129
|
-
toolResult: { toolCallId: 'b', content: 'rb' },
|
|
130
|
-
});
|
|
131
|
-
// No results → drop the whole round back to [user].
|
|
132
|
-
assert.is(trimToRestorableBoundary([user, callAB]).length, 1);
|
|
133
|
-
// Only A answered → B orphaned → drop the round back to [user].
|
|
134
|
-
assert.is(trimToRestorableBoundary([user, callAB, resultA]).length, 1);
|
|
135
|
-
// Both answered → kept (ends on a balanced tool result).
|
|
136
|
-
assert.is(trimToRestorableBoundary([user, callAB, resultA, resultB]).length, 4);
|
|
137
|
-
});
|
|
138
|
-
Suite('trimToRestorableBoundary drops a trailing empty assistant placeholder', () => {
|
|
139
|
-
const user = asMsg({ role: 'user', content: 'hi' });
|
|
140
|
-
const placeholder = asMsg({ role: 'assistant', content: '' });
|
|
141
|
-
assert.is(trimToRestorableBoundary([user, placeholder]).length, 1);
|
|
142
|
-
});
|
|
143
|
-
// GENC-1351: orphaned agent snapshots (dynamic agent arrays — e.g. genesis-create).
|
|
144
|
-
Suite('unknownAgentSnapshots flags saved agents missing from the configured set', () => {
|
|
145
|
-
const snap = {
|
|
146
|
-
agentSnapshots: { 'Guided Booking': {}, Bootstrap: {} },
|
|
147
|
-
};
|
|
148
|
-
// Bootstrap gone from the current set → flagged; Guided Booking is known.
|
|
149
|
-
assert.equal(unknownAgentSnapshots(snap, ['Guided Booking', 'Trades']), ['Bootstrap']);
|
|
150
|
-
// All known → none flagged.
|
|
151
|
-
assert.equal(unknownAgentSnapshots(snap, ['Guided Booking', 'Bootstrap']), []);
|
|
152
|
-
// Empty known set → can't classify yet → none (avoids false positives mid-init).
|
|
153
|
-
assert.equal(unknownAgentSnapshots(snap, []), []);
|
|
154
|
-
});
|
|
155
|
-
// GENC-1351 §6 — `reconcileAgentSnapshots`: the save-time guard against a
|
|
156
|
-
// post-restore autosave wiping a mid-flow machine. The bug (regression #1): a
|
|
157
|
-
// restore reads a good snapshot, then the restore's own history change fires an
|
|
158
|
-
// autosave BEFORE the agent re-activates, so the live capture is empty and, left
|
|
159
|
-
// unguarded, `{}` overwrites the machine → the flow resets on the next reload.
|
|
160
|
-
const MACHINE = { machine: { value: 'pickInstrument', context: { counterparty: { id: 'c1' } } } };
|
|
161
|
-
Suite('reconcileAgentSnapshots: REGRESSION — carries the flow-owner forward when live capture is empty', () => {
|
|
162
|
-
// Post-restore, pre-activation: getActiveAgent() is undefined → live is {}, but
|
|
163
|
-
// a flow is owned and the good image sits in the cache (seeded on restore).
|
|
164
|
-
const { snapshots, cache } = reconcileAgentSnapshots({}, 'Guided Booking', {
|
|
165
|
-
'Guided Booking': MACHINE,
|
|
166
|
-
});
|
|
167
|
-
assert.equal(snapshots['Guided Booking'], MACHINE, 'the flow-owner machine is preserved, NOT wiped to {}');
|
|
168
|
-
assert.equal(cache['Guided Booking'], MACHINE, 'cache keeps the good image');
|
|
169
|
-
});
|
|
170
|
-
Suite('reconcileAgentSnapshots: a live capture wins and advances the cache', () => {
|
|
171
|
-
const FRESH = { machine: { value: 'enterDetails', context: {} } };
|
|
172
|
-
const { snapshots, cache } = reconcileAgentSnapshots({ 'Guided Booking': FRESH }, 'Guided Booking', {
|
|
173
|
-
'Guided Booking': MACHINE, // stale cached value
|
|
174
|
-
});
|
|
175
|
-
assert.equal(snapshots['Guided Booking'], FRESH, 'live image is used (never the stale cache)');
|
|
176
|
-
assert.equal(cache['Guided Booking'], FRESH, 'cache advances to the freshly-captured image');
|
|
177
|
-
});
|
|
178
|
-
Suite('reconcileAgentSnapshots: no flow owner → empty stays empty (no stale carry-forward)', () => {
|
|
179
|
-
// Idle / stateless save: nothing to carry, even if the cache holds an old image.
|
|
180
|
-
const { snapshots, cache } = reconcileAgentSnapshots({}, null, { 'Guided Booking': MACHINE });
|
|
181
|
-
assert.equal(snapshots, {}, 'nothing carried forward without a flow owner');
|
|
182
|
-
assert.equal(cache['Guided Booking'], MACHINE, 'an empty save never erases the cache');
|
|
183
|
-
});
|
|
184
|
-
Suite('reconcileAgentSnapshots: flow owner set but nothing cached → cannot fabricate state', () => {
|
|
185
|
-
const { snapshots } = reconcileAgentSnapshots({}, 'Guided Booking', {});
|
|
186
|
-
assert.is(snapshots['Guided Booking'], undefined, 'no image to carry → left absent, not invented');
|
|
187
|
-
});
|
|
188
|
-
Suite('reconcileAgentSnapshots: carries the flow owner while keeping a co-captured other agent', () => {
|
|
189
|
-
const OTHER = { machine: { value: 'x' } };
|
|
190
|
-
const { snapshots } = reconcileAgentSnapshots({ Other: OTHER }, 'Guided Booking', {
|
|
191
|
-
'Guided Booking': MACHINE,
|
|
192
|
-
});
|
|
193
|
-
assert.equal(snapshots.Other, OTHER, 'the live non-owner image is kept');
|
|
194
|
-
assert.equal(snapshots['Guided Booking'], MACHINE, 'the missing flow-owner is carried forward');
|
|
195
|
-
});
|
|
196
|
-
Suite('reconcileAgentSnapshots: does not mutate its inputs', () => {
|
|
197
|
-
const live = {};
|
|
198
|
-
const cacheIn = { 'Guided Booking': MACHINE };
|
|
199
|
-
reconcileAgentSnapshots(live, 'Guided Booking', cacheIn);
|
|
200
|
-
assert.equal(live, {}, 'live input untouched');
|
|
201
|
-
assert.equal(Object.keys(cacheIn), ['Guided Booking'], 'cache input untouched');
|
|
202
|
-
});
|
|
203
|
-
// GENC-1351 §5.9: the pure notice-resolution helper (message + pre-fill prompt).
|
|
204
|
-
// The freeze/marker mechanics live on the element (integration), not here.
|
|
205
|
-
Suite('resolveNonResumableNotice: custom message wins, blank falls back to default', () => {
|
|
206
|
-
const msgs = [
|
|
207
|
-
asMsg({ role: 'user', content: 'first' }),
|
|
208
|
-
asMsg({ role: 'assistant', content: 'x' }),
|
|
209
|
-
];
|
|
210
|
-
assert.is(resolveNonResumableNotice({
|
|
211
|
-
notResumableMessage: 'custom',
|
|
212
|
-
messages: msgs,
|
|
213
|
-
defaultMessage: 'D',
|
|
214
|
-
}).resumeMessage, 'custom');
|
|
215
|
-
assert.is(resolveNonResumableNotice({ notResumableMessage: ' ', messages: msgs, defaultMessage: 'D' })
|
|
216
|
-
.resumeMessage, 'D', 'a blank custom message falls back to the default');
|
|
217
|
-
assert.is(resolveNonResumableNotice({ messages: msgs, defaultMessage: 'D' }).resumeMessage, 'D', 'no custom message → default');
|
|
218
|
-
});
|
|
219
|
-
Suite('resolveNonResumableNotice: prompt prefers the captured flow prompt, else last user msg', () => {
|
|
220
|
-
const msgs = [
|
|
221
|
-
asMsg({ role: 'user', content: 'earlier' }),
|
|
222
|
-
asMsg({ role: 'assistant', content: 'x' }),
|
|
223
|
-
asMsg({ role: 'user', content: 'latest user' }),
|
|
224
|
-
];
|
|
225
|
-
assert.is(resolveNonResumableNotice({
|
|
226
|
-
flowActivationPrompt: 'the flow prompt',
|
|
227
|
-
messages: msgs,
|
|
228
|
-
defaultMessage: 'D',
|
|
229
|
-
}).resumePrompt, 'the flow prompt', 'captured flow-activating prompt takes precedence');
|
|
230
|
-
assert.is(resolveNonResumableNotice({ flowActivationPrompt: null, messages: msgs, defaultMessage: 'D' })
|
|
231
|
-
.resumePrompt, 'latest user', 'no flow prompt → falls back to the last user message');
|
|
232
|
-
assert.is(resolveNonResumableNotice({ messages: [], defaultMessage: 'D' }).resumePrompt, undefined, 'no flow prompt and no user message → undefined');
|
|
233
|
-
});
|
|
234
|
-
// GENC-1351 §5.9: the freeze fallback keeps the transcript through the last user
|
|
235
|
-
// message, dropping the agent's in-flight turns (issue #3).
|
|
236
|
-
Suite('keepThroughLastUserMessage drops trailing agent turns, keeps through the last user msg', () => {
|
|
237
|
-
const u1 = asMsg({ role: 'user', content: 'book a flight' });
|
|
238
|
-
const a1 = asMsg({ role: 'assistant', content: 'ok' });
|
|
239
|
-
const u2 = asMsg({ role: 'user', content: 'show fills for T1' });
|
|
240
|
-
const plan = asMsg({
|
|
241
|
-
role: 'assistant',
|
|
242
|
-
content: '',
|
|
243
|
-
toolCalls: [{ id: 'a', name: 'f', args: {} }],
|
|
244
|
-
});
|
|
245
|
-
const res = asMsg({ role: 'tool', content: '' });
|
|
246
|
-
// Trailing agent turns after the last user message → dropped.
|
|
247
|
-
assert.equal(keepThroughLastUserMessage([u1, a1, u2, plan, res]), [u1, a1, u2]);
|
|
248
|
-
// Already ends on a user message → unchanged (same ref).
|
|
249
|
-
const endsUser = [u1, a1, u2];
|
|
250
|
-
assert.is(keepThroughLastUserMessage(endsUser), endsUser);
|
|
251
|
-
// No user message at all → unchanged (nothing to trim to).
|
|
252
|
-
const noUser = [a1, res];
|
|
253
|
-
assert.is(keepThroughLastUserMessage(noUser), noUser);
|
|
254
|
-
assert.equal(keepThroughLastUserMessage([]).length, 0);
|
|
255
|
-
});
|
|
256
|
-
Suite.run();
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { __awaiter } from "tslib";
|
|
2
|
-
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
3
|
-
// Side-effect import — MUST precede the driver import so it subclasses jsdom's
|
|
4
|
-
// EventTarget (CustomEvent dispatch then works in node).
|
|
5
|
-
import '../../components/chat-driver/align-event-globals';
|
|
6
|
-
import { OrchestratingDriver } from '../../components/orchestrating-driver/orchestrating-driver';
|
|
7
|
-
import { clearDiagnosticsCursorRegistry } from './diagnostics-cursors';
|
|
8
|
-
import { clearPersisterRegistry } from './persister-registry';
|
|
9
|
-
import { SessionPersister } from './session-persister';
|
|
10
|
-
// GENC-1351 — end-to-end save→restore→route→re-activate proof. The persister unit
|
|
11
|
-
// tests exercise `persistSession`/`restore` against a FAKE driver; the driver pin/
|
|
12
|
-
// flow-owner tests exercise routing against a real driver. NOTHING wired the two
|
|
13
|
-
// together — the exact integration that broke once (bug #2: the live agent's
|
|
14
|
-
// `serialize()` was read via `driver.getActiveAgent()` — a host minting fresh
|
|
15
|
-
// configs handed back a never-activated closure whose `serialize()` returned
|
|
16
|
-
// undefined, so the machine reset on reload). This test closes that gap with a REAL
|
|
17
|
-
// `OrchestratingDriver` + a REAL `SessionPersister` + an in-memory provider:
|
|
18
|
-
// persist (capture the LIVE agent's state) → restore (prime + re-lock routing) →
|
|
19
|
-
// send (route back to the flow-owner without re-classifying) → re-activate aligned.
|
|
20
|
-
// `Planner` is `excludeFromClassifier` AND never pinned, so a turn landing on it after
|
|
21
|
-
// restore proves the RESTORED flow-owner lock (not the pin, not the classifier) routed
|
|
22
|
-
// it — the `setFlowOwner`-on-restore half of the fix.
|
|
23
|
-
const KEY = 'e2e-session';
|
|
24
|
-
const makeRegistry = (provider) => ({
|
|
25
|
-
get: () => provider,
|
|
26
|
-
default: () => provider,
|
|
27
|
-
defaultName: () => 'test',
|
|
28
|
-
names: () => ['test'],
|
|
29
|
-
getStatus: () => __awaiter(void 0, void 0, void 0, function* () { return null; }),
|
|
30
|
-
listStatuses: () => __awaiter(void 0, void 0, void 0, function* () { return []; }),
|
|
31
|
-
});
|
|
32
|
-
/** A provider that ends every turn immediately with a plain-text reply (no tools). */
|
|
33
|
-
const noToolProvider = () => ({
|
|
34
|
-
chat: (_history, _userMessage, _options) => __awaiter(void 0, void 0, void 0, function* () { return ({ role: 'assistant', content: 'ok' }); }),
|
|
35
|
-
});
|
|
36
|
-
const agentCfg = (overrides) => (Object.assign({ description: 'test agent' }, overrides));
|
|
37
|
-
/** In-memory persistence provider (single session), enough for one save + one load. */
|
|
38
|
-
function makeMemoryProvider() {
|
|
39
|
-
let stored = null;
|
|
40
|
-
return {
|
|
41
|
-
saveDebounceMs: 0,
|
|
42
|
-
save: (_k, snap) => __awaiter(this, void 0, void 0, function* () {
|
|
43
|
-
stored = snap;
|
|
44
|
-
}),
|
|
45
|
-
load: () => __awaiter(this, void 0, void 0, function* () { return stored; }),
|
|
46
|
-
clear: () => __awaiter(this, void 0, void 0, function* () {
|
|
47
|
-
stored = null;
|
|
48
|
-
}),
|
|
49
|
-
get current() {
|
|
50
|
-
return stored;
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
/** The functional store shape the persister reads scalars from / hydrates into. */
|
|
55
|
-
function makeStoreFake(over = {}) {
|
|
56
|
-
return {
|
|
57
|
-
store: {
|
|
58
|
-
aiAssistant: Object.assign({ messages: [], flowOwnerAgentName: null, flowActivationPrompt: null, activeAgent: undefined, pinnedAgentName: null,
|
|
59
|
-
// The persister reads `sessionUsage.costUsd` when building a snapshot, so this
|
|
60
|
-
// has to be the object rather than a bare cost scalar.
|
|
61
|
-
sessionUsage: {
|
|
62
|
-
costUsd: 0,
|
|
63
|
-
uncachedInputTokens: 0,
|
|
64
|
-
cacheReadTokens: 0,
|
|
65
|
-
cacheWriteTokens: 0,
|
|
66
|
-
outputTokens: 0,
|
|
67
|
-
}, contextTokens: 0, contextLimit: 0, activeModel: undefined, activeProviderName: undefined, restoring: false }, over),
|
|
68
|
-
},
|
|
69
|
-
actions: {
|
|
70
|
-
aiAssistant: {
|
|
71
|
-
loadSession: () => { },
|
|
72
|
-
setInputValue: () => { },
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
function makePersister(driver, storeFake, provider) {
|
|
78
|
-
const deps = {
|
|
79
|
-
stateKey: KEY,
|
|
80
|
-
getPersistence: () => ({ enabled: true, provider: provider }),
|
|
81
|
-
getAgentNames: () => ['Genesis Assistant', 'Planner'],
|
|
82
|
-
getDriver: () => driver,
|
|
83
|
-
getStore: () => storeFake,
|
|
84
|
-
getDiagnosticEntries: () => [],
|
|
85
|
-
};
|
|
86
|
-
return new SessionPersister(deps);
|
|
87
|
-
}
|
|
88
|
-
const tick = () => new Promise((r) => {
|
|
89
|
-
setTimeout(r, 0);
|
|
90
|
-
});
|
|
91
|
-
const Suite = createLogicSuite('stateful-restore-e2e');
|
|
92
|
-
Suite.before.each(() => {
|
|
93
|
-
clearPersisterRegistry();
|
|
94
|
-
clearDiagnosticsCursorRegistry();
|
|
95
|
-
});
|
|
96
|
-
Suite('a flow-owned stateful agent survives persist → restore → resume, routing back without re-classifying', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
-
var _a, _b;
|
|
98
|
-
const provider = makeMemoryProvider();
|
|
99
|
-
// ── Persist side ────────────────────────────────────────────────────────────
|
|
100
|
-
// `stateA` is the Planner's live state bag; `serialize` closes over it, exactly
|
|
101
|
-
// like a real stateful agent's machine snapshot.
|
|
102
|
-
let stateA = { cursor: 0 };
|
|
103
|
-
const planner1 = agentCfg({
|
|
104
|
-
name: 'Planner',
|
|
105
|
-
excludeFromClassifier: true,
|
|
106
|
-
onActivate: (_a) => __awaiter(void 0, [_a], void 0, function* ({ restored }) {
|
|
107
|
-
if (restored !== undefined)
|
|
108
|
-
stateA = restored;
|
|
109
|
-
}),
|
|
110
|
-
serialize: () => stateA,
|
|
111
|
-
systemPrompt: () => 'planner',
|
|
112
|
-
});
|
|
113
|
-
const driver1 = new OrchestratingDriver(makeRegistry(noToolProvider()), [agentCfg({ name: 'Genesis Assistant' }), planner1], { sessionKey: KEY });
|
|
114
|
-
// Flow-owned but NOT pinned, then run a turn: the flow-owner lock routes the
|
|
115
|
-
// Planner (it's excludeFromClassifier, so the classifier could never pick it).
|
|
116
|
-
driver1.setFlowOwner('Planner');
|
|
117
|
-
yield driver1.sendMessage('go');
|
|
118
|
-
assert.is((_a = driver1.getActiveAgent()) === null || _a === void 0 ? void 0 : _a.name, 'Planner', 'flow-owner routed the Planner on persist side');
|
|
119
|
-
// The flow advances — mutate the SAME object `serialize()` returns.
|
|
120
|
-
stateA.cursor = 3;
|
|
121
|
-
// The store mirrors what main.ts keeps: the flow-owner lock lives in the store
|
|
122
|
-
// (that's the scalar `persistSession` reads).
|
|
123
|
-
const storeFake1 = makeStoreFake({ flowOwnerAgentName: 'Planner' });
|
|
124
|
-
const persister1 = makePersister(driver1, storeFake1, provider);
|
|
125
|
-
persister1.scheduleSave();
|
|
126
|
-
yield tick();
|
|
127
|
-
yield tick();
|
|
128
|
-
const saved = provider.current;
|
|
129
|
-
assert.ok(saved, 'a snapshot was persisted');
|
|
130
|
-
assert.is(saved.flowOwnerAgentName, 'Planner', 'flow-owner lock persisted');
|
|
131
|
-
// Bug-#2 assertion: the LIVE (activated) agent's serialize was captured via
|
|
132
|
-
// driver.getActiveAgent(), so the advanced state is in the snapshot.
|
|
133
|
-
assert.equal(saved.agentSnapshots.Planner, { cursor: 3 }, "live agent's advanced state captured (not a never-activated closure)");
|
|
134
|
-
// ── Restore side (fresh instances share the same in-memory provider) ──────────
|
|
135
|
-
let stateB = { cursor: -1 }; // sentinel; restore must overwrite
|
|
136
|
-
const planner2 = agentCfg({
|
|
137
|
-
name: 'Planner',
|
|
138
|
-
excludeFromClassifier: true,
|
|
139
|
-
onActivate: (_a) => __awaiter(void 0, [_a], void 0, function* ({ restored }) {
|
|
140
|
-
if (restored !== undefined)
|
|
141
|
-
stateB = restored;
|
|
142
|
-
}),
|
|
143
|
-
serialize: () => stateB,
|
|
144
|
-
systemPrompt: () => 'planner',
|
|
145
|
-
});
|
|
146
|
-
const driver2 = new OrchestratingDriver(makeRegistry(noToolProvider()), [agentCfg({ name: 'Genesis Assistant' }), planner2], { sessionKey: KEY });
|
|
147
|
-
const storeFake2 = makeStoreFake();
|
|
148
|
-
const persister2 = makePersister(driver2, storeFake2, provider);
|
|
149
|
-
yield persister2.restore();
|
|
150
|
-
// The restored flow-owner lock (setFlowOwner-on-restore) routes the next turn
|
|
151
|
-
// back to the Planner without re-classifying.
|
|
152
|
-
yield driver2.sendMessage('again');
|
|
153
|
-
assert.is((_b = driver2.getActiveAgent()) === null || _b === void 0 ? void 0 : _b.name, 'Planner', 'restored flow-owner routed the Planner (excludeFromClassifier + unpinned ⇒ only the restored lock could have)');
|
|
154
|
-
// primeRestoredAgentState → onActivate({ restored }) fed the snapshot back, so
|
|
155
|
-
// the fresh Planner resumed exactly where the persisted one left off.
|
|
156
|
-
assert.equal(stateB, { cursor: 3 }, 'restored agent re-activated aligned to the persisted state');
|
|
157
|
-
}));
|
|
158
|
-
Suite.run();
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
2
|
-
import { resolveExclusiveLoadingStyle } from './animation-exclusivity';
|
|
3
|
-
const suite = createLogicSuite('resolveExclusiveLoadingStyle');
|
|
4
|
-
suite('leaves a selection with only the dots loading style untouched', () => {
|
|
5
|
-
const next = ['loading', 'halo'];
|
|
6
|
-
assert.equal(resolveExclusiveLoadingStyle(next, ['halo']), ['loading', 'halo']);
|
|
7
|
-
});
|
|
8
|
-
suite('leaves a selection with only the waves loading style untouched', () => {
|
|
9
|
-
const next = ['waves', 'halo'];
|
|
10
|
-
assert.equal(resolveExclusiveLoadingStyle(next, ['halo']), ['waves', 'halo']);
|
|
11
|
-
});
|
|
12
|
-
suite('leaves a selection with no loading style untouched', () => {
|
|
13
|
-
assert.equal(resolveExclusiveLoadingStyle(['halo'], []), ['halo']);
|
|
14
|
-
});
|
|
15
|
-
suite('drops dots when waves was just added on top of dots', () => {
|
|
16
|
-
// Previously dots was on; user ticks waves → waves wins, dots removed.
|
|
17
|
-
const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'halo'], ['loading', 'halo']);
|
|
18
|
-
assert.equal(result, ['waves', 'halo']);
|
|
19
|
-
});
|
|
20
|
-
suite('drops waves when dots was just added on top of waves', () => {
|
|
21
|
-
// Previously waves was on; user ticks dots → dots wins, waves removed.
|
|
22
|
-
const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'halo'], ['waves', 'halo']);
|
|
23
|
-
assert.equal(result, ['loading', 'halo']);
|
|
24
|
-
});
|
|
25
|
-
suite('preserves the order of the surviving selection', () => {
|
|
26
|
-
const result = resolveExclusiveLoadingStyle(['halo', 'loading', 'waves'], ['halo', 'loading']);
|
|
27
|
-
// waves was just added → loading dropped; halo and waves keep their order.
|
|
28
|
-
assert.equal(result, ['halo', 'waves']);
|
|
29
|
-
});
|
|
30
|
-
suite('falls back to dots when both are present with no prior state (e.g. bad config)', () => {
|
|
31
|
-
const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'halo'], []);
|
|
32
|
-
assert.equal(result, ['loading', 'halo']);
|
|
33
|
-
});
|
|
34
|
-
suite('falls back to dots when both were already present (neither newly added)', () => {
|
|
35
|
-
const result = resolveExclusiveLoadingStyle(['loading', 'waves'], ['loading', 'waves', 'halo']);
|
|
36
|
-
assert.equal(result, ['loading']);
|
|
37
|
-
});
|
|
38
|
-
suite('handles an empty selection', () => {
|
|
39
|
-
assert.equal(resolveExclusiveLoadingStyle([], ['loading']), []);
|
|
40
|
-
});
|
|
41
|
-
suite('switching between any two loading styles keeps only the newly added one', () => {
|
|
42
|
-
// flowingWaves was on; user picks plasma → plasma wins, flowingWaves dropped.
|
|
43
|
-
assert.equal(resolveExclusiveLoadingStyle(['flowingWaves', 'plasma', 'halo'], ['flowingWaves', 'halo']), ['plasma', 'halo']);
|
|
44
|
-
// plasma was on; user picks the dots → dots win, plasma dropped.
|
|
45
|
-
assert.equal(resolveExclusiveLoadingStyle(['plasma', 'loading'], ['plasma']), ['loading']);
|
|
46
|
-
});
|
|
47
|
-
suite('drops all but the newly added style when several are somehow selected', () => {
|
|
48
|
-
const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'flowingWaves', 'plasma'], ['loading', 'waves', 'plasma']);
|
|
49
|
-
// flowingWaves is the only one absent from `previous`, so it wins.
|
|
50
|
-
assert.equal(result, ['flowingWaves']);
|
|
51
|
-
});
|
|
52
|
-
suite.run();
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
2
|
-
import { collectSessionModels } from './collect-session-models';
|
|
3
|
-
const assistantMsg = (overrides = {}) => (Object.assign({ role: 'assistant', content: '' }, overrides));
|
|
4
|
-
const statusEntry = (name, model, provider) => ({
|
|
5
|
-
name,
|
|
6
|
-
isDefault: false,
|
|
7
|
-
status: { model, provider },
|
|
8
|
-
});
|
|
9
|
-
const suite = createLogicSuite('collectSessionModels');
|
|
10
|
-
suite('collects unique models from assistant messages', () => {
|
|
11
|
-
var _a, _b, _c, _d;
|
|
12
|
-
const models = collectSessionModels([
|
|
13
|
-
assistantMsg({ model: 'claude-sonnet-4-6' }),
|
|
14
|
-
assistantMsg({ model: 'claude-haiku-4-5-20251001' }),
|
|
15
|
-
assistantMsg({ model: 'claude-sonnet-4-6', provider: 'anthropic' }),
|
|
16
|
-
assistantMsg({ model: 'gemini-2.5-pro', providerName: 'high', provider: 'gemini' }),
|
|
17
|
-
], []);
|
|
18
|
-
assert.is(models.length, 3);
|
|
19
|
-
assert.is((_a = models[0]) === null || _a === void 0 ? void 0 : _a.model, 'claude-sonnet-4-6');
|
|
20
|
-
assert.is((_b = models[0]) === null || _b === void 0 ? void 0 : _b.provider, 'anthropic');
|
|
21
|
-
assert.is((_c = models[2]) === null || _c === void 0 ? void 0 : _c.model, 'gemini-2.5-pro');
|
|
22
|
-
assert.is((_d = models[2]) === null || _d === void 0 ? void 0 : _d.provider, 'gemini');
|
|
23
|
-
});
|
|
24
|
-
suite('resolves vendor from providerStatuses when messages lack provider', () => {
|
|
25
|
-
var _a;
|
|
26
|
-
const models = collectSessionModels([assistantMsg({ model: 'gemini-3.1-flash-lite', providerName: 'low' })], [statusEntry('low', 'gemini-3.1-flash-lite', 'gemini')]);
|
|
27
|
-
assert.is(models.length, 1);
|
|
28
|
-
assert.is((_a = models[0]) === null || _a === void 0 ? void 0 : _a.provider, 'gemini');
|
|
29
|
-
});
|
|
30
|
-
suite('does not persist registry tier names as provider', () => {
|
|
31
|
-
var _a, _b;
|
|
32
|
-
const models = collectSessionModels([assistantMsg({ model: 'gemini-3.1-flash-lite', providerName: 'low' })], []);
|
|
33
|
-
assert.is((_a = models[0]) === null || _a === void 0 ? void 0 : _a.provider, undefined);
|
|
34
|
-
assert.is('providerName' in ((_b = models[0]) !== null && _b !== void 0 ? _b : {}), false);
|
|
35
|
-
});
|
|
36
|
-
suite('includes models from sub-agent traces', () => {
|
|
37
|
-
var _a;
|
|
38
|
-
const models = collectSessionModels([
|
|
39
|
-
assistantMsg({
|
|
40
|
-
toolCalls: [
|
|
41
|
-
{
|
|
42
|
-
id: 'tc1',
|
|
43
|
-
name: 'spawn',
|
|
44
|
-
args: {},
|
|
45
|
-
subAgentTrace: [assistantMsg({ model: 'claude-haiku-4-5-20251001' })],
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
}),
|
|
49
|
-
], []);
|
|
50
|
-
assert.is(models.length, 1);
|
|
51
|
-
assert.is((_a = models[0]) === null || _a === void 0 ? void 0 : _a.model, 'claude-haiku-4-5-20251001');
|
|
52
|
-
});
|
|
53
|
-
suite.run();
|