@bike4mind/cli 0.2.31-cli-update-command.19453 → 0.2.31-cli-update-command.19494
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/{artifactExtractor-22AVFN7A.js → artifactExtractor-45XJBQXX.js} +1 -1
- package/dist/{chunk-24JZFYBV.js → chunk-3LGHTME3.js} +57 -2
- package/dist/{chunk-Q6YCIGC3.js → chunk-4G6Y7DHE.js} +6 -6
- package/dist/{chunk-U4HDDXWT.js → chunk-E6SLQJIU.js} +2 -2
- package/dist/{chunk-DEW32L4X.js → chunk-J3NGNWDX.js} +2 -2
- package/dist/{chunk-6HWTNX47.js → chunk-PHQTB34R.js} +2 -2
- package/dist/{chunk-E77VWEKZ.js → chunk-UVOOKS6R.js} +109 -13
- package/dist/commands/doctorCommand.js +1 -1
- package/dist/commands/updateCommand.js +1 -1
- package/dist/{create-LTISVVKL.js → create-O3HFNHEE.js} +3 -3
- package/dist/index.js +899 -35
- package/dist/{llmMarkdownGenerator-DF7EFQZW.js → llmMarkdownGenerator-J6LUJVSM.js} +1 -1
- package/dist/{markdownGenerator-6TAH7OEH.js → markdownGenerator-LE7NXUBV.js} +1 -1
- package/dist/{mementoService-BY5ACS3K.js → mementoService-ULVLQKR5.js} +3 -3
- package/dist/{src-ZXFQ5Y4O.js → src-2WR4ASOI.js} +13 -1
- package/dist/{src-C5QSTGEZ.js → src-W6VVQ66O.js} +6 -2
- package/dist/{subtractCredits-V645IMXQ.js → subtractCredits-HG4Q3SA3.js} +3 -3
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "./chunk-GQGOWACU.js";
|
|
3
|
-
import "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
import "./chunk-J3NGNWDX.js";
|
|
4
|
+
import "./chunk-E6SLQJIU.js";
|
|
5
5
|
import "./chunk-BPFEGDC7.js";
|
|
6
6
|
import "./chunk-BDQBOLYG.js";
|
|
7
7
|
import {
|
|
8
8
|
getEffectiveApiKey,
|
|
9
9
|
getOpenWeatherKey,
|
|
10
10
|
getSerperKey
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-PHQTB34R.js";
|
|
12
12
|
import {
|
|
13
13
|
ConfigStore,
|
|
14
14
|
logger
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
checkForUpdate,
|
|
18
18
|
package_default
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-4G6Y7DHE.js";
|
|
20
20
|
import {
|
|
21
21
|
selectActiveBackgroundAgents,
|
|
22
22
|
useCliStore
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
OpenAIBackend,
|
|
33
33
|
OpenAIImageService,
|
|
34
34
|
XAIImageService
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-3LGHTME3.js";
|
|
36
36
|
import {
|
|
37
37
|
AiEvents,
|
|
38
38
|
ApiKeyEvents,
|
|
@@ -90,7 +90,7 @@ import {
|
|
|
90
90
|
getMcpProviderMetadata,
|
|
91
91
|
getViewById,
|
|
92
92
|
resolveNavigationIntents
|
|
93
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-UVOOKS6R.js";
|
|
94
94
|
import {
|
|
95
95
|
Logger
|
|
96
96
|
} from "./chunk-OCYRD7D6.js";
|
|
@@ -100,7 +100,7 @@ import React21, { useState as useState10, useEffect as useEffect7, useCallback a
|
|
|
100
100
|
import { render, Box as Box20, Text as Text20, useApp, useInput as useInput9 } from "ink";
|
|
101
101
|
import { execSync } from "child_process";
|
|
102
102
|
import { randomBytes as randomBytes5 } from "crypto";
|
|
103
|
-
import { v4 as
|
|
103
|
+
import { v4 as uuidv413 } from "uuid";
|
|
104
104
|
|
|
105
105
|
// src/components/App.tsx
|
|
106
106
|
import React15, { useState as useState6, useEffect as useEffect5 } from "react";
|
|
@@ -10186,7 +10186,9 @@ var SolverIdSchema = z139.enum([
|
|
|
10186
10186
|
"tabu",
|
|
10187
10187
|
"genetic-algorithm",
|
|
10188
10188
|
"ant-colony",
|
|
10189
|
-
"highs"
|
|
10189
|
+
"highs",
|
|
10190
|
+
"simulated-qaoa",
|
|
10191
|
+
"ionq-qaoa"
|
|
10190
10192
|
]);
|
|
10191
10193
|
|
|
10192
10194
|
// ../../b4m-core/packages/quantum/dist/src/solvers/greedy-solver.js
|
|
@@ -10837,6 +10839,396 @@ function constructSolution(allOps, pheromone, heuristic, rng) {
|
|
|
10837
10839
|
return result;
|
|
10838
10840
|
}
|
|
10839
10841
|
|
|
10842
|
+
// ../../b4m-core/packages/quantum/dist/src/solvers/metadata.js
|
|
10843
|
+
var solverMetadata = {
|
|
10844
|
+
naive: {
|
|
10845
|
+
icon: "\u{1F40C}",
|
|
10846
|
+
// 🐌
|
|
10847
|
+
color: "neutral",
|
|
10848
|
+
complexity: "Exponential - O(n!)",
|
|
10849
|
+
requires: "Browser compute",
|
|
10850
|
+
tagline: "Try every possible solution",
|
|
10851
|
+
fullDescription: "The simplest possible approach: systematically enumerate and evaluate every valid permutation of operations. Guaranteed to find the optimal solution, but becomes computationally infeasible for larger problems due to factorial growth.",
|
|
10852
|
+
howItWorks: [
|
|
10853
|
+
"Generate all valid operation orderings (permutations)",
|
|
10854
|
+
"Evaluate each ordering to compute makespan",
|
|
10855
|
+
"Track the best solution found",
|
|
10856
|
+
"Return the globally optimal solution"
|
|
10857
|
+
],
|
|
10858
|
+
keyParameters: [{ name: "Search Space", value: "n! permutations for n operations (with pruning for precedence)" }],
|
|
10859
|
+
strengths: ["Guarantees optimal solution", "Simple to understand and implement", "No parameter tuning needed"],
|
|
10860
|
+
weaknesses: [
|
|
10861
|
+
"O(n!) time complexity",
|
|
10862
|
+
"Infeasible for problems with >10 operations",
|
|
10863
|
+
"No early termination heuristics"
|
|
10864
|
+
],
|
|
10865
|
+
bestFor: "Tiny problems (\u226410 ops) where you need a guaranteed optimal baseline",
|
|
10866
|
+
wikipedia: "https://en.wikipedia.org/wiki/Brute-force_search"
|
|
10867
|
+
},
|
|
10868
|
+
greedy: {
|
|
10869
|
+
icon: "\u26A1",
|
|
10870
|
+
// ⚡
|
|
10871
|
+
color: "success",
|
|
10872
|
+
complexity: "O(n\xB2)",
|
|
10873
|
+
requires: "Browser compute",
|
|
10874
|
+
tagline: "Always pick the locally best choice",
|
|
10875
|
+
fullDescription: 'A fast heuristic that builds a solution by always selecting the "best" available operation at each step. Uses the Shortest Processing Time (SPT) rule: schedule the shortest available operation first. Fast but may miss global optimum.',
|
|
10876
|
+
howItWorks: [
|
|
10877
|
+
"Start with an empty schedule",
|
|
10878
|
+
"Find all operations whose predecessors are complete",
|
|
10879
|
+
"Pick the one with shortest duration (SPT rule)",
|
|
10880
|
+
"Schedule it at the earliest possible time",
|
|
10881
|
+
"Repeat until all operations scheduled"
|
|
10882
|
+
],
|
|
10883
|
+
keyParameters: [{ name: "Priority Rule", value: "SPT (Shortest Processing Time) \u2014 favors quick operations" }],
|
|
10884
|
+
strengths: ["Extremely fast (O(n\xB2))", "Produces valid solutions instantly", "Good baseline for comparison"],
|
|
10885
|
+
weaknesses: ["No backtracking", "Often far from optimal", "Can make locally optimal but globally poor choices"],
|
|
10886
|
+
bestFor: "Quick baseline, real-time scheduling, or as initial solution for metaheuristics",
|
|
10887
|
+
wikipedia: "https://en.wikipedia.org/wiki/Greedy_algorithm"
|
|
10888
|
+
},
|
|
10889
|
+
"random-restart": {
|
|
10890
|
+
icon: "\u{1F3B2}",
|
|
10891
|
+
// 🎲
|
|
10892
|
+
color: "primary",
|
|
10893
|
+
complexity: "~5 seconds",
|
|
10894
|
+
requires: "Browser compute",
|
|
10895
|
+
tagline: "Climb hills, restart when stuck",
|
|
10896
|
+
fullDescription: "Combines local search with random restarts. From each starting point, repeatedly move to better neighboring solutions until no improvement is possible (local optimum). Then restart from a new random solution. The best solution across all restarts is returned.",
|
|
10897
|
+
howItWorks: [
|
|
10898
|
+
"Generate a random valid schedule",
|
|
10899
|
+
"Try swapping pairs of operations",
|
|
10900
|
+
"Accept swaps that improve makespan",
|
|
10901
|
+
"When stuck (no improving swaps), restart from new random solution",
|
|
10902
|
+
"Track best solution across all restarts"
|
|
10903
|
+
],
|
|
10904
|
+
keyParameters: [
|
|
10905
|
+
{ name: "Max Restarts", value: "Number of times to restart from random (100)" },
|
|
10906
|
+
{ name: "Stagnation Limit", value: "Iterations without improvement before restart (1000)" }
|
|
10907
|
+
],
|
|
10908
|
+
strengths: ["Escapes local optima via restarts", "Simple and parallelizable", "Better than pure hill climbing"],
|
|
10909
|
+
weaknesses: [
|
|
10910
|
+
"No information sharing between restarts",
|
|
10911
|
+
"May revisit similar solutions",
|
|
10912
|
+
"Random restarts are uninformed"
|
|
10913
|
+
],
|
|
10914
|
+
bestFor: "Medium problems where pure hill climbing gets stuck",
|
|
10915
|
+
wikipedia: "https://en.wikipedia.org/wiki/Hill_climbing#Random-restart_hill_climbing"
|
|
10916
|
+
},
|
|
10917
|
+
"simulated-annealing": {
|
|
10918
|
+
icon: "\u{1F525}",
|
|
10919
|
+
// 🔥
|
|
10920
|
+
color: "warning",
|
|
10921
|
+
complexity: "~7 seconds",
|
|
10922
|
+
requires: "Browser compute",
|
|
10923
|
+
tagline: "Cool down from chaos to order",
|
|
10924
|
+
fullDescription: 'Inspired by metallurgy: heating metal and slowly cooling it produces stronger crystal structures. SA starts "hot" (accepting bad moves) and "cools" (becoming pickier). This allows escaping local optima early while converging to good solutions later.',
|
|
10925
|
+
howItWorks: [
|
|
10926
|
+
"Start with high temperature (T=100)",
|
|
10927
|
+
"Generate neighbor by swapping two operations",
|
|
10928
|
+
"If better, always accept",
|
|
10929
|
+
"If worse, accept with probability e^(-\u0394/T)",
|
|
10930
|
+
"Gradually reduce temperature (cooling)",
|
|
10931
|
+
"As T\u21920, behaves like pure hill climbing"
|
|
10932
|
+
],
|
|
10933
|
+
keyParameters: [
|
|
10934
|
+
{ name: "Initial Temperature", value: 'Starting "heat" level (100)' },
|
|
10935
|
+
{ name: "Cooling Rate", value: "How fast temperature decreases (~0.9999...)" },
|
|
10936
|
+
{ name: "Iterations", value: "5 million moves evaluated" }
|
|
10937
|
+
],
|
|
10938
|
+
strengths: [
|
|
10939
|
+
"Escapes local optima probabilistically",
|
|
10940
|
+
"Well-studied theoretical properties",
|
|
10941
|
+
"Single parameter to tune (cooling schedule)"
|
|
10942
|
+
],
|
|
10943
|
+
weaknesses: ["Cooling schedule is problem-dependent", "Can be slow to converge", "Single-solution method"],
|
|
10944
|
+
bestFor: "General optimization when you have moderate compute budget",
|
|
10945
|
+
wikipedia: "https://en.wikipedia.org/wiki/Simulated_annealing"
|
|
10946
|
+
},
|
|
10947
|
+
"simulated-annealing-medium": {
|
|
10948
|
+
icon: "\u{1F525}",
|
|
10949
|
+
// 🔥
|
|
10950
|
+
color: "warning",
|
|
10951
|
+
complexity: "~70 seconds",
|
|
10952
|
+
requires: "Browser compute",
|
|
10953
|
+
tagline: "Extended cooling for deeper exploration",
|
|
10954
|
+
fullDescription: "Same algorithm as SA but with 10\xD7 more iterations (50M). The slower cooling schedule allows more thorough exploration of the solution space before settling into a final basin.",
|
|
10955
|
+
howItWorks: ["Same as SA standard", "But with 50 million iterations", "Slower cooling = more exploration time"],
|
|
10956
|
+
keyParameters: [
|
|
10957
|
+
{ name: "Iterations", value: "50 million moves (10\xD7 standard)" },
|
|
10958
|
+
{ name: "Timeout", value: "5 minutes max" }
|
|
10959
|
+
],
|
|
10960
|
+
strengths: ["More thorough than standard SA", "Better for complex landscapes", "Higher chance of global optimum"],
|
|
10961
|
+
weaknesses: [
|
|
10962
|
+
"10\xD7 slower than standard SA",
|
|
10963
|
+
"Diminishing returns on easy problems",
|
|
10964
|
+
"May be overkill for small instances"
|
|
10965
|
+
],
|
|
10966
|
+
bestFor: "When standard SA finds good but not great solutions",
|
|
10967
|
+
wikipedia: "https://en.wikipedia.org/wiki/Simulated_annealing"
|
|
10968
|
+
},
|
|
10969
|
+
"simulated-annealing-large": {
|
|
10970
|
+
icon: "\u{1F525}",
|
|
10971
|
+
// 🔥
|
|
10972
|
+
color: "danger",
|
|
10973
|
+
complexity: "~12 minutes",
|
|
10974
|
+
requires: "Browser compute",
|
|
10975
|
+
tagline: "Maximum exploration budget",
|
|
10976
|
+
fullDescription: "The heavy artillery: 500 million iterations with up to 30 minutes of compute. For when you absolutely need the best possible solution and have time to wait.",
|
|
10977
|
+
howItWorks: [
|
|
10978
|
+
"Same as SA standard/medium",
|
|
10979
|
+
"But with 500 million iterations",
|
|
10980
|
+
"Very slow cooling for maximum exploration"
|
|
10981
|
+
],
|
|
10982
|
+
keyParameters: [
|
|
10983
|
+
{ name: "Iterations", value: "500 million moves (100\xD7 standard)" },
|
|
10984
|
+
{ name: "Timeout", value: "30 minutes max" }
|
|
10985
|
+
],
|
|
10986
|
+
strengths: ["Maximum exploration", "Best chance at global optimum", "Leaves no stone unturned"],
|
|
10987
|
+
weaknesses: ["Very slow (up to 30 min)", "Massive overkill for small problems", "Diminishing returns"],
|
|
10988
|
+
bestFor: "Large problems where quality matters more than time",
|
|
10989
|
+
wikipedia: "https://en.wikipedia.org/wiki/Simulated_annealing"
|
|
10990
|
+
},
|
|
10991
|
+
tabu: {
|
|
10992
|
+
icon: "\u{1F50D}",
|
|
10993
|
+
// 🔍
|
|
10994
|
+
color: "primary",
|
|
10995
|
+
complexity: "~20\u201340 seconds",
|
|
10996
|
+
requires: "Browser compute",
|
|
10997
|
+
tagline: "Remember mistakes to avoid repeating them",
|
|
10998
|
+
fullDescription: 'This is a classical (simple) Tabu Search following Glover (1986) with aspiration by objective. It maintains a short-term recency-based memory of recent swap moves that are temporarily forbidden ("tabu"). The neighborhood is generated by random pairwise swaps of operations in the permutation representation. Unlike reactive or robust variants, this implementation uses a fixed tabu tenure and does not employ long-term memory, intensification, or diversification phases.',
|
|
10999
|
+
howItWorks: [
|
|
11000
|
+
"Start from a random permutation of operations (seed: 42 for reproducibility)",
|
|
11001
|
+
"Generate 20 random neighbor solutions by swapping two operation positions",
|
|
11002
|
+
"Select the best non-tabu neighbor (even if worse than current \u2014 forced exploration)",
|
|
11003
|
+
"Record the swap as tabu for 15 iterations (fixed tenure)",
|
|
11004
|
+
"Aspiration criterion: override tabu if move produces a new global best",
|
|
11005
|
+
"Repeat for 10,000 iterations or until timeout (15s)",
|
|
11006
|
+
"Periodically clean expired entries from the tabu list"
|
|
11007
|
+
],
|
|
11008
|
+
keyParameters: [
|
|
11009
|
+
{ name: "Variant", value: "Simple Tabu Search (Glover 1986) with aspiration by objective" },
|
|
11010
|
+
{ name: "Tabu Structure", value: "Swap-based \u2014 records position pairs (i,j) that were swapped" },
|
|
11011
|
+
{ name: "Tabu Tenure", value: "Fixed at 15 iterations (no reactive adjustment)" },
|
|
11012
|
+
{ name: "Neighborhood Size", value: "20 random swap candidates evaluated per iteration" },
|
|
11013
|
+
{ name: "Total Iterations", value: "10,000" },
|
|
11014
|
+
{ name: "Aspiration", value: "Accept tabu move if it improves the global best" },
|
|
11015
|
+
{ name: "Memory Type", value: "Short-term recency only (no frequency-based long-term memory)" }
|
|
11016
|
+
],
|
|
11017
|
+
strengths: [
|
|
11018
|
+
"Memory prevents cycling back to recently visited solutions",
|
|
11019
|
+
"Forced acceptance of worse moves enables escaping local optima",
|
|
11020
|
+
"Aspiration criterion preserves exploitation of exceptional solutions",
|
|
11021
|
+
"Deterministic with seed (reproducible results)"
|
|
11022
|
+
],
|
|
11023
|
+
weaknesses: [
|
|
11024
|
+
"Fixed tenure \u2014 no adaptive/reactive adjustment to search dynamics",
|
|
11025
|
+
"No long-term memory (frequency-based diversification)",
|
|
11026
|
+
"No intensification phase to deep-search promising regions",
|
|
11027
|
+
"Small neighborhood (20) may miss good moves in large search spaces"
|
|
11028
|
+
],
|
|
11029
|
+
bestFor: "Medium-complexity problems with many local optima and plateau regions",
|
|
11030
|
+
wikipedia: "https://en.wikipedia.org/wiki/Tabu_search",
|
|
11031
|
+
otherResources: [
|
|
11032
|
+
{ label: "Glover (1986) \u2014 Original paper", url: "https://doi.org/10.1016/0305-0548(86)90048-1" },
|
|
11033
|
+
{
|
|
11034
|
+
label: "Glover & Laguna \u2014 Tabu Search (book)",
|
|
11035
|
+
url: "https://en.wikipedia.org/wiki/Tabu_search#References"
|
|
11036
|
+
}
|
|
11037
|
+
]
|
|
11038
|
+
},
|
|
11039
|
+
"genetic-algorithm": {
|
|
11040
|
+
icon: "\u{1F9EC}",
|
|
11041
|
+
// 🧬
|
|
11042
|
+
color: "success",
|
|
11043
|
+
complexity: "~30 seconds",
|
|
11044
|
+
requires: "Browser compute",
|
|
11045
|
+
tagline: "Evolve solutions through natural selection",
|
|
11046
|
+
fullDescription: 'Inspired by biological evolution: maintain a population of solutions that reproduce, mutate, and compete. Better solutions are more likely to survive and pass on their "genes" (good partial solutions). Over generations, the population evolves toward optimality.',
|
|
11047
|
+
howItWorks: [
|
|
11048
|
+
"Initialize population of 100 random schedules",
|
|
11049
|
+
"Evaluate fitness (1/makespan) for each",
|
|
11050
|
+
"Selection: pick parents via tournament (best of 5 random)",
|
|
11051
|
+
"Crossover: combine two parents to create child",
|
|
11052
|
+
"Mutation: randomly swap operations (20% chance)",
|
|
11053
|
+
"Elitism: top 5 solutions survive unchanged",
|
|
11054
|
+
"Repeat for 15,000 generations"
|
|
11055
|
+
],
|
|
11056
|
+
keyParameters: [
|
|
11057
|
+
{ name: "Population Size", value: "Number of solutions maintained (100)" },
|
|
11058
|
+
{ name: "Generations", value: "Evolution cycles (15,000)" },
|
|
11059
|
+
{ name: "Crossover Rate", value: "Probability of combining parents (80%)" },
|
|
11060
|
+
{ name: "Mutation Rate", value: "Probability of random change (20%)" },
|
|
11061
|
+
{ name: "Elite Count", value: "Best solutions preserved unchanged (5)" }
|
|
11062
|
+
],
|
|
11063
|
+
strengths: ["Population maintains diversity", "Crossover combines good building blocks", "Naturally parallel"],
|
|
11064
|
+
weaknesses: ["Many parameters to tune", "Can converge prematurely", "Crossover design is problem-specific"],
|
|
11065
|
+
bestFor: "Complex problems where good solutions share common substructures",
|
|
11066
|
+
wikipedia: "https://en.wikipedia.org/wiki/Genetic_algorithm",
|
|
11067
|
+
otherResources: [
|
|
11068
|
+
{
|
|
11069
|
+
label: "Order Crossover (OX)",
|
|
11070
|
+
url: "https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#Order_crossover_(OX)"
|
|
11071
|
+
}
|
|
11072
|
+
]
|
|
11073
|
+
},
|
|
11074
|
+
"ant-colony": {
|
|
11075
|
+
icon: "\u{1F41C}",
|
|
11076
|
+
// 🐜
|
|
11077
|
+
color: "success",
|
|
11078
|
+
complexity: "~30 seconds",
|
|
11079
|
+
requires: "Browser compute",
|
|
11080
|
+
tagline: "Follow the pheromone trails",
|
|
11081
|
+
fullDescription: "Inspired by how ants find shortest paths to food: they deposit pheromones, and other ants preferentially follow stronger trails. Over time, shorter paths accumulate more pheromone (ants traverse them faster), creating positive feedback toward good solutions.",
|
|
11082
|
+
howItWorks: [
|
|
11083
|
+
"Initialize pheromone trails uniformly",
|
|
11084
|
+
"Each ant builds a complete schedule probabilistically",
|
|
11085
|
+
"Operations with more pheromone are more likely to be chosen",
|
|
11086
|
+
"After all ants finish, evaluate their solutions",
|
|
11087
|
+
"Deposit pheromone on edges used by good solutions",
|
|
11088
|
+
"Evaporate some pheromone (forget old information)",
|
|
11089
|
+
"Best ant's solution gets extra pheromone (elitism)"
|
|
11090
|
+
],
|
|
11091
|
+
keyParameters: [
|
|
11092
|
+
{ name: "Number of Ants", value: "Solutions built per iteration (50)" },
|
|
11093
|
+
{ name: "Iterations", value: "Pheromone update cycles (100,000)" },
|
|
11094
|
+
{ name: "Alpha (\u03B1)", value: "Pheromone importance (1.0)" },
|
|
11095
|
+
{ name: "Beta (\u03B2)", value: "Heuristic importance (2.0)" },
|
|
11096
|
+
{ name: "Evaporation Rate", value: "Pheromone decay per iteration (10%)" }
|
|
11097
|
+
],
|
|
11098
|
+
strengths: ["Implicit parallelism", "Positive feedback accelerates convergence", "Robust to problem changes"],
|
|
11099
|
+
weaknesses: ["Many parameters", "Can converge prematurely", "Pheromone model must match problem"],
|
|
11100
|
+
bestFor: 'Routing and sequencing problems with clear "edge" structure',
|
|
11101
|
+
wikipedia: "https://en.wikipedia.org/wiki/Ant_colony_optimization_algorithms",
|
|
11102
|
+
otherResources: [{ label: "Marco Dorigo (inventor)", url: "https://en.wikipedia.org/wiki/Marco_Dorigo" }]
|
|
11103
|
+
},
|
|
11104
|
+
highs: {
|
|
11105
|
+
icon: "\u{1F3AF}",
|
|
11106
|
+
// 🎯
|
|
11107
|
+
color: "success",
|
|
11108
|
+
complexity: "Seconds to minutes",
|
|
11109
|
+
requires: "Browser compute (WASM)",
|
|
11110
|
+
available: false,
|
|
11111
|
+
tagline: "Open-source solver rivaling commercial giants",
|
|
11112
|
+
fullDescription: "HiGHS (High-performance Interior-point, Gradient-descent, Simplex) is an MIT-licensed open-source solver that achieves 90%+ of commercial solver performance. Developed at the University of Edinburgh, it has rapidly become the leading open-source alternative to Gurobi and COPT for linear and mixed-integer programming.",
|
|
11113
|
+
howItWorks: [
|
|
11114
|
+
"Formulate problem as Integer Linear Program (ILP/MIP)",
|
|
11115
|
+
"Presolve: simplify model, tighten bounds, detect infeasibility",
|
|
11116
|
+
"LP relaxation using dual simplex or interior-point method",
|
|
11117
|
+
"Branch-and-bound with sophisticated node selection",
|
|
11118
|
+
"Cutting planes: Gomory cuts, MIR cuts, cover cuts",
|
|
11119
|
+
"Return optimal (or near-optimal) solution with gap certificate"
|
|
11120
|
+
],
|
|
11121
|
+
keyParameters: [
|
|
11122
|
+
{ name: "Time Limit", value: "Maximum solve time before returning best found" },
|
|
11123
|
+
{ name: "MIP Gap", value: "Stop when proven within X% of optimal (e.g., 0.01 = 1%)" },
|
|
11124
|
+
{ name: "Threads", value: "Number of parallel threads (default: all cores)" }
|
|
11125
|
+
],
|
|
11126
|
+
strengths: [
|
|
11127
|
+
"MIT licensed \u2014 fully open source, embed anywhere",
|
|
11128
|
+
"90%+ of Gurobi/COPT performance on most problems",
|
|
11129
|
+
"Active development with rapid improvements",
|
|
11130
|
+
"Can deploy in customer infrastructure (no license servers)"
|
|
11131
|
+
],
|
|
11132
|
+
weaknesses: [
|
|
11133
|
+
"Slightly slower than Gurobi on very large instances",
|
|
11134
|
+
"Fewer specialized cuts than commercial solvers",
|
|
11135
|
+
"Less mature ecosystem (but growing fast)"
|
|
11136
|
+
],
|
|
11137
|
+
bestFor: "Production scheduling when you need commercial-grade results without commercial licensing",
|
|
11138
|
+
wikipedia: "https://en.wikipedia.org/wiki/HiGHS_optimization_solver",
|
|
11139
|
+
otherResources: [
|
|
11140
|
+
{ label: "HiGHS GitHub", url: "https://github.com/ERGO-Code/HiGHS" },
|
|
11141
|
+
{ label: "HiGHS Documentation", url: "https://highs.dev/" },
|
|
11142
|
+
{ label: "Mittelmann MIP Benchmarks", url: "http://plato.asu.edu/ftp/milp.html" }
|
|
11143
|
+
]
|
|
11144
|
+
},
|
|
11145
|
+
"simulated-qaoa": {
|
|
11146
|
+
icon: "\u{1F52E}",
|
|
11147
|
+
// 🔮
|
|
11148
|
+
color: "primary",
|
|
11149
|
+
complexity: "~30\u201360 seconds",
|
|
11150
|
+
requires: "Backend compute",
|
|
11151
|
+
available: false,
|
|
11152
|
+
tagline: "Quantum-inspired classical simulation",
|
|
11153
|
+
fullDescription: "A classical simulator of the Quantum Approximate Optimization Algorithm (QAOA). Encodes the job-shop scheduling problem as a QUBO (Quadratic Unconstrained Binary Optimization), then simulates the quantum variational circuit classically. Provides a preview of quantum advantage without requiring real quantum hardware.",
|
|
11154
|
+
howItWorks: [
|
|
11155
|
+
"Encode scheduling problem as QUBO matrix",
|
|
11156
|
+
"Initialize simulated quantum state |+\u27E9\u2297\u207F",
|
|
11157
|
+
"Apply p layers of QAOA circuit (problem + mixer unitaries)",
|
|
11158
|
+
"Classically optimize variational parameters (\u03B3, \u03B2)",
|
|
11159
|
+
"Measure (sample) the final state to extract candidate schedules",
|
|
11160
|
+
"Decode best bitstring back to a valid schedule"
|
|
11161
|
+
],
|
|
11162
|
+
keyParameters: [
|
|
11163
|
+
{ name: "QAOA Depth (p)", value: "Number of alternating layers (default: 3)" },
|
|
11164
|
+
{ name: "Optimizer", value: "COBYLA for variational parameter optimization" },
|
|
11165
|
+
{ name: "Shots", value: "Number of measurement samples (1024)" },
|
|
11166
|
+
{ name: "Qubits", value: "One per possible (job, timeslot) assignment" }
|
|
11167
|
+
],
|
|
11168
|
+
strengths: [
|
|
11169
|
+
"Preview quantum algorithms without quantum hardware",
|
|
11170
|
+
"Explores solution space via quantum superposition (simulated)",
|
|
11171
|
+
"Useful for benchmarking against real quantum results",
|
|
11172
|
+
"Runs entirely in-browser or on backend \u2014 no API keys needed"
|
|
11173
|
+
],
|
|
11174
|
+
weaknesses: [
|
|
11175
|
+
"Exponential classical overhead \u2014 limited to ~20 qubits",
|
|
11176
|
+
"Cannot capture true quantum speedup",
|
|
11177
|
+
"QUBO encoding may not be optimal for all problem structures",
|
|
11178
|
+
"Variational optimization can get stuck in local minima"
|
|
11179
|
+
],
|
|
11180
|
+
bestFor: "Small problems where you want to preview quantum approaches before investing in real hardware",
|
|
11181
|
+
wikipedia: "https://en.wikipedia.org/wiki/Quantum_approximate_optimization_algorithm",
|
|
11182
|
+
otherResources: [
|
|
11183
|
+
{ label: "Farhi et al. (2014) \u2014 Original QAOA Paper", url: "https://arxiv.org/abs/1411.4028" },
|
|
11184
|
+
{ label: "QUBO Formulation Guide", url: "https://en.wikipedia.org/wiki/Quadratic_unconstrained_binary_optimization" }
|
|
11185
|
+
]
|
|
11186
|
+
},
|
|
11187
|
+
"ionq-qaoa": {
|
|
11188
|
+
icon: "\u269B\uFE0F",
|
|
11189
|
+
// ⚛️
|
|
11190
|
+
color: "danger",
|
|
11191
|
+
complexity: "Minutes (queue + execution)",
|
|
11192
|
+
requires: "IonQ API key + credits",
|
|
11193
|
+
available: false,
|
|
11194
|
+
tagline: "Real quantum hardware optimization",
|
|
11195
|
+
fullDescription: "Runs the Quantum Approximate Optimization Algorithm on IonQ\u2019s trapped-ion quantum computers. This is real quantum computation \u2014 the problem is encoded as a QUBO, compiled to native quantum gates, and executed on physical qubits. Results include genuine quantum effects like superposition and entanglement.",
|
|
11196
|
+
howItWorks: [
|
|
11197
|
+
"Encode scheduling problem as QUBO matrix",
|
|
11198
|
+
"Compile QAOA circuit to IonQ\u2019s native gate set (MS, GPi, GPi2)",
|
|
11199
|
+
"Submit job to IonQ cloud via API",
|
|
11200
|
+
"Queue for hardware execution (trapped-ion processor)",
|
|
11201
|
+
"Execute quantum circuit with real qubits",
|
|
11202
|
+
"Retrieve measurement results and decode to schedule"
|
|
11203
|
+
],
|
|
11204
|
+
keyParameters: [
|
|
11205
|
+
{ name: "QAOA Depth (p)", value: "Number of alternating layers (default: 1\u20132 for NISQ)" },
|
|
11206
|
+
{ name: "Backend", value: "IonQ Harmony (11 qubits) or Aria (25 qubits)" },
|
|
11207
|
+
{ name: "Shots", value: "Number of circuit executions (1024)" },
|
|
11208
|
+
{ name: "Error Mitigation", value: "IonQ debiasing enabled by default" }
|
|
11209
|
+
],
|
|
11210
|
+
strengths: [
|
|
11211
|
+
"Real quantum computation with genuine quantum effects",
|
|
11212
|
+
"Trapped-ion qubits have high gate fidelity (~99.5%)",
|
|
11213
|
+
"All-to-all connectivity \u2014 no SWAP overhead",
|
|
11214
|
+
"Potential for quantum advantage on certain problem classes"
|
|
11215
|
+
],
|
|
11216
|
+
weaknesses: [
|
|
11217
|
+
"Requires IonQ API key and credits (not free)",
|
|
11218
|
+
"Queue wait times can be minutes to hours",
|
|
11219
|
+
"Limited to ~25 qubits (current hardware)",
|
|
11220
|
+
"NISQ noise limits circuit depth and solution quality"
|
|
11221
|
+
],
|
|
11222
|
+
bestFor: "Exploring real quantum optimization on small problem instances when you have IonQ access",
|
|
11223
|
+
wikipedia: "https://en.wikipedia.org/wiki/Trapped-ion_quantum_computer",
|
|
11224
|
+
otherResources: [
|
|
11225
|
+
{ label: "IonQ Documentation", url: "https://docs.ionq.com/" },
|
|
11226
|
+
{ label: "IonQ Aria Specs", url: "https://ionq.com/quantum-systems/aria" },
|
|
11227
|
+
{ label: "QAOA on NISQ Devices", url: "https://arxiv.org/abs/1812.01041" }
|
|
11228
|
+
]
|
|
11229
|
+
}
|
|
11230
|
+
};
|
|
11231
|
+
|
|
10840
11232
|
// ../../b4m-core/packages/quantum/dist/src/solvers/index.js
|
|
10841
11233
|
var allSolvers = [
|
|
10842
11234
|
greedySolver,
|
|
@@ -10856,9 +11248,15 @@ function getSolver(id) {
|
|
|
10856
11248
|
function getAvailableSolverIds() {
|
|
10857
11249
|
return Array.from(solverRegistry.keys());
|
|
10858
11250
|
}
|
|
11251
|
+
var displaySolvers = [
|
|
11252
|
+
...allSolvers.map((s) => ({ id: s.id, name: s.name, description: s.description })),
|
|
11253
|
+
{ id: "highs", name: "HiGHS (WASM)", description: solverMetadata.highs.tagline },
|
|
11254
|
+
{ id: "simulated-qaoa", name: "Simulated QAOA", description: solverMetadata["simulated-qaoa"].tagline },
|
|
11255
|
+
{ id: "ionq-qaoa", name: "IonQ QAOA", description: solverMetadata["ionq-qaoa"].tagline }
|
|
11256
|
+
];
|
|
10859
11257
|
|
|
10860
11258
|
// ../../b4m-core/packages/quantum/dist/src/prompts/system-prompt.js
|
|
10861
|
-
var QUANTUM_CANVASSER_SYSTEM_PROMPT = `You are
|
|
11259
|
+
var QUANTUM_CANVASSER_SYSTEM_PROMPT = `You are OptiHashi, an AI agent specializing in combinatorial optimization and job-shop scheduling. You help users formulate scheduling problems, run solver algorithms, and interpret optimization results. You are solver-agnostic \u2014 you route problems to the best solver whether classical (greedy, simulated annealing, genetic algorithms, HiGHS MIP) or quantum (QAOA), building credibility through honest recommendations.
|
|
10862
11260
|
|
|
10863
11261
|
## Your Capabilities
|
|
10864
11262
|
|
|
@@ -13290,9 +13688,7 @@ var QuestStartBodySchema = z140.object({
|
|
|
13290
13688
|
fabFileIds: z140.array(z140.string()).optional()
|
|
13291
13689
|
})).optional(),
|
|
13292
13690
|
/** User's timezone (IANA format, e.g., "America/New_York") */
|
|
13293
|
-
timezone: z140.string().optional()
|
|
13294
|
-
/** Pre-fetched API key table from invoke phase — avoids redundant DB call in process (#6616 P1-a) */
|
|
13295
|
-
apiKeyTable: z140.record(z140.string(), z140.string().nullable()).optional()
|
|
13691
|
+
timezone: z140.string().optional()
|
|
13296
13692
|
});
|
|
13297
13693
|
|
|
13298
13694
|
// ../../b4m-core/packages/services/dist/src/llm/StatusManager.js
|
|
@@ -13990,6 +14386,10 @@ var ServerToolExecutor = class {
|
|
|
13990
14386
|
};
|
|
13991
14387
|
|
|
13992
14388
|
// src/llm/ToolRouter.ts
|
|
14389
|
+
var wsToolExecutor = null;
|
|
14390
|
+
function setWebSocketToolExecutor(executor) {
|
|
14391
|
+
wsToolExecutor = executor;
|
|
14392
|
+
}
|
|
13993
14393
|
var SERVER_TOOLS = ["weather_info", "web_search", "web_fetch"];
|
|
13994
14394
|
var LOCAL_TOOLS = [
|
|
13995
14395
|
"file_read",
|
|
@@ -14012,7 +14412,15 @@ function isLocalTool(toolName) {
|
|
|
14012
14412
|
}
|
|
14013
14413
|
async function executeTool(toolName, input, apiClient, localToolFn) {
|
|
14014
14414
|
if (isServerTool(toolName)) {
|
|
14015
|
-
|
|
14415
|
+
if (wsToolExecutor) {
|
|
14416
|
+
logger.debug(`[ToolRouter] Routing ${toolName} to server via WebSocket`);
|
|
14417
|
+
const result = await wsToolExecutor.execute(toolName, input);
|
|
14418
|
+
if (!result.success) {
|
|
14419
|
+
return `Error executing ${toolName}: ${result.error || "Tool execution failed"}`;
|
|
14420
|
+
}
|
|
14421
|
+
return typeof result.content === "string" ? result.content : JSON.stringify(result.content ?? "");
|
|
14422
|
+
}
|
|
14423
|
+
logger.debug(`[ToolRouter] Routing ${toolName} to server via HTTP`);
|
|
14016
14424
|
const executor = new ServerToolExecutor(apiClient);
|
|
14017
14425
|
return await executor.executeTool(toolName, input);
|
|
14018
14426
|
} else if (isLocalTool(toolName)) {
|
|
@@ -15974,6 +16382,173 @@ var ServerLlmBackend = class {
|
|
|
15974
16382
|
}
|
|
15975
16383
|
};
|
|
15976
16384
|
|
|
16385
|
+
// src/llm/WebSocketLlmBackend.ts
|
|
16386
|
+
import { v4 as uuidv411 } from "uuid";
|
|
16387
|
+
function stripThinkingBlocks2(text) {
|
|
16388
|
+
return text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
16389
|
+
}
|
|
16390
|
+
var WebSocketLlmBackend = class {
|
|
16391
|
+
constructor(options) {
|
|
16392
|
+
this.wsManager = options.wsManager;
|
|
16393
|
+
this.apiClient = options.apiClient;
|
|
16394
|
+
this.currentModel = options.model;
|
|
16395
|
+
this.tokenGetter = options.tokenGetter;
|
|
16396
|
+
this.wsCompletionUrl = options.wsCompletionUrl;
|
|
16397
|
+
}
|
|
16398
|
+
/**
|
|
16399
|
+
* Send completion request via HTTP POST, receive streaming response via WebSocket.
|
|
16400
|
+
* Collects all streamed chunks, then calls callback once at completion
|
|
16401
|
+
* with the full accumulated content.
|
|
16402
|
+
*/
|
|
16403
|
+
async complete(model, messages, options, callback) {
|
|
16404
|
+
logger.debug(`[WebSocketLlmBackend] Starting complete() with model: ${model}`);
|
|
16405
|
+
if (options.abortSignal?.aborted) {
|
|
16406
|
+
logger.debug("[WebSocketLlmBackend] Request aborted before start");
|
|
16407
|
+
return;
|
|
16408
|
+
}
|
|
16409
|
+
if (!this.wsManager.isConnected) {
|
|
16410
|
+
throw new Error("WebSocket is not connected");
|
|
16411
|
+
}
|
|
16412
|
+
const requestId = uuidv411();
|
|
16413
|
+
return new Promise((resolve3, reject) => {
|
|
16414
|
+
const isVerbose = process.env.B4M_VERBOSE === "1";
|
|
16415
|
+
const isUltraVerbose = process.env.B4M_DEBUG_STREAM === "1";
|
|
16416
|
+
const streamLogger = new StreamLogger(logger, "WebSocketLlmBackend", isVerbose, isUltraVerbose);
|
|
16417
|
+
streamLogger.streamStart();
|
|
16418
|
+
let eventCount = 0;
|
|
16419
|
+
let accumulatedText = "";
|
|
16420
|
+
let lastUsageInfo = {};
|
|
16421
|
+
let toolsUsed = [];
|
|
16422
|
+
let thinkingBlocks = [];
|
|
16423
|
+
let settled = false;
|
|
16424
|
+
const settle = (action) => {
|
|
16425
|
+
if (settled) return;
|
|
16426
|
+
settled = true;
|
|
16427
|
+
this.wsManager.offRequest(requestId);
|
|
16428
|
+
this.wsManager.offDisconnect(onDisconnect);
|
|
16429
|
+
action();
|
|
16430
|
+
};
|
|
16431
|
+
const settleResolve = () => settle(() => resolve3());
|
|
16432
|
+
const settleReject = (err) => settle(() => reject(err));
|
|
16433
|
+
const onDisconnect = () => {
|
|
16434
|
+
logger.debug("[WebSocketLlmBackend] Connection dropped during completion");
|
|
16435
|
+
settleReject(new Error("WebSocket connection lost during completion"));
|
|
16436
|
+
};
|
|
16437
|
+
this.wsManager.onDisconnect(onDisconnect);
|
|
16438
|
+
if (options.abortSignal) {
|
|
16439
|
+
if (options.abortSignal.aborted) {
|
|
16440
|
+
settleResolve();
|
|
16441
|
+
return;
|
|
16442
|
+
}
|
|
16443
|
+
options.abortSignal.addEventListener(
|
|
16444
|
+
"abort",
|
|
16445
|
+
() => {
|
|
16446
|
+
logger.debug("[WebSocketLlmBackend] Abort signal received");
|
|
16447
|
+
settleResolve();
|
|
16448
|
+
},
|
|
16449
|
+
{ once: true }
|
|
16450
|
+
);
|
|
16451
|
+
}
|
|
16452
|
+
const updateUsage = (usage) => {
|
|
16453
|
+
if (usage) {
|
|
16454
|
+
lastUsageInfo = { inputTokens: usage.inputTokens, outputTokens: usage.outputTokens };
|
|
16455
|
+
}
|
|
16456
|
+
};
|
|
16457
|
+
this.wsManager.onRequest(requestId, (message) => {
|
|
16458
|
+
if (options.abortSignal?.aborted) return;
|
|
16459
|
+
const action = message.action;
|
|
16460
|
+
if (action === "cli_completion_chunk") {
|
|
16461
|
+
eventCount++;
|
|
16462
|
+
const chunk = message.chunk;
|
|
16463
|
+
streamLogger.onEvent(eventCount, JSON.stringify(chunk));
|
|
16464
|
+
const textChunk = chunk.text || "";
|
|
16465
|
+
if (textChunk) accumulatedText += textChunk;
|
|
16466
|
+
updateUsage(chunk.usage);
|
|
16467
|
+
if (chunk.type === "content") {
|
|
16468
|
+
streamLogger.onContent(eventCount, textChunk, accumulatedText);
|
|
16469
|
+
} else if (chunk.type === "tool_use") {
|
|
16470
|
+
streamLogger.onCriticalEvent(eventCount, "TOOL_USE", `tools: ${chunk.tools?.length}`);
|
|
16471
|
+
if (chunk.tools && chunk.tools.length > 0) toolsUsed = chunk.tools;
|
|
16472
|
+
if (chunk.thinking && chunk.thinking.length > 0) thinkingBlocks = chunk.thinking;
|
|
16473
|
+
}
|
|
16474
|
+
} else if (action === "cli_completion_done") {
|
|
16475
|
+
streamLogger.streamComplete(accumulatedText);
|
|
16476
|
+
const cleanedText = stripThinkingBlocks2(accumulatedText);
|
|
16477
|
+
if (!cleanedText && toolsUsed.length === 0) {
|
|
16478
|
+
settleResolve();
|
|
16479
|
+
return;
|
|
16480
|
+
}
|
|
16481
|
+
const info = {
|
|
16482
|
+
...lastUsageInfo,
|
|
16483
|
+
...toolsUsed.length > 0 && { toolsUsed },
|
|
16484
|
+
...thinkingBlocks.length > 0 && { thinking: thinkingBlocks }
|
|
16485
|
+
};
|
|
16486
|
+
callback([cleanedText], info).then(() => settleResolve()).catch((err) => settleReject(err));
|
|
16487
|
+
} else if (action === "cli_completion_error") {
|
|
16488
|
+
const errorMsg = message.error || "Server error";
|
|
16489
|
+
streamLogger.onCriticalEvent(eventCount, "ERROR", errorMsg);
|
|
16490
|
+
settleReject(new Error(errorMsg));
|
|
16491
|
+
}
|
|
16492
|
+
});
|
|
16493
|
+
const axiosInstance = this.apiClient.getAxiosInstance();
|
|
16494
|
+
axiosInstance.post(
|
|
16495
|
+
this.wsCompletionUrl,
|
|
16496
|
+
{
|
|
16497
|
+
requestId,
|
|
16498
|
+
model,
|
|
16499
|
+
messages,
|
|
16500
|
+
options: {
|
|
16501
|
+
temperature: options.temperature,
|
|
16502
|
+
maxTokens: options.maxTokens,
|
|
16503
|
+
stream: true,
|
|
16504
|
+
tools: options.tools || []
|
|
16505
|
+
}
|
|
16506
|
+
},
|
|
16507
|
+
{ signal: options.abortSignal }
|
|
16508
|
+
).catch((err) => {
|
|
16509
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
16510
|
+
settleReject(new Error(`HTTP request failed: ${msg}`));
|
|
16511
|
+
});
|
|
16512
|
+
});
|
|
16513
|
+
}
|
|
16514
|
+
/**
|
|
16515
|
+
* Get available models from server (REST call, not streaming).
|
|
16516
|
+
* Delegates to ApiClient -- same as ServerLlmBackend.
|
|
16517
|
+
*/
|
|
16518
|
+
async getModelInfo() {
|
|
16519
|
+
try {
|
|
16520
|
+
logger.debug("[WebSocketLlmBackend] Fetching models from /api/models");
|
|
16521
|
+
const response = await this.apiClient.get("/api/models");
|
|
16522
|
+
if (!response || typeof response !== "object" || !Array.isArray(response.models)) {
|
|
16523
|
+
logger.warn("[WebSocketLlmBackend] Invalid API response format, using fallback models");
|
|
16524
|
+
return this.getFallbackModels();
|
|
16525
|
+
}
|
|
16526
|
+
const filteredModels = response.models.filter(
|
|
16527
|
+
(model) => model.type === "text" && model.supportsTools === true
|
|
16528
|
+
);
|
|
16529
|
+
if (filteredModels.length === 0) {
|
|
16530
|
+
logger.warn("[WebSocketLlmBackend] No CLI-compatible models found, using fallback");
|
|
16531
|
+
return this.getFallbackModels();
|
|
16532
|
+
}
|
|
16533
|
+
logger.debug(`[WebSocketLlmBackend] Loaded ${filteredModels.length} models`);
|
|
16534
|
+
return filteredModels;
|
|
16535
|
+
} catch (error) {
|
|
16536
|
+
logger.warn(
|
|
16537
|
+
`[WebSocketLlmBackend] Failed to fetch models: ${error instanceof Error ? error.message : String(error)}`
|
|
16538
|
+
);
|
|
16539
|
+
return this.getFallbackModels();
|
|
16540
|
+
}
|
|
16541
|
+
}
|
|
16542
|
+
getFallbackModels() {
|
|
16543
|
+
return [
|
|
16544
|
+
{ id: "claude-sonnet-4-5-20250929", name: "Claude 4.5 Sonnet" },
|
|
16545
|
+
{ id: "claude-3-5-haiku-20241022", name: "Claude 3.5 Haiku" },
|
|
16546
|
+
{ id: "gpt-4o", name: "GPT-4o" },
|
|
16547
|
+
{ id: "gpt-4o-mini", name: "GPT-4o Mini" }
|
|
16548
|
+
];
|
|
16549
|
+
}
|
|
16550
|
+
};
|
|
16551
|
+
|
|
15977
16552
|
// src/llm/NotifyingLlmBackend.ts
|
|
15978
16553
|
var NotifyingLlmBackend = class {
|
|
15979
16554
|
constructor(inner, backgroundManager) {
|
|
@@ -16008,6 +16583,253 @@ Please acknowledge these background agent results and incorporate them into your
|
|
|
16008
16583
|
}
|
|
16009
16584
|
};
|
|
16010
16585
|
|
|
16586
|
+
// src/ws/WebSocketConnectionManager.ts
|
|
16587
|
+
var WebSocketConnectionManager = class {
|
|
16588
|
+
constructor(wsUrl, getToken) {
|
|
16589
|
+
this.ws = null;
|
|
16590
|
+
this.heartbeatInterval = null;
|
|
16591
|
+
this.reconnectAttempts = 0;
|
|
16592
|
+
this.maxReconnectDelay = 3e4;
|
|
16593
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
16594
|
+
this.disconnectHandlers = /* @__PURE__ */ new Set();
|
|
16595
|
+
this.reconnectTimer = null;
|
|
16596
|
+
this.connected = false;
|
|
16597
|
+
this.connecting = false;
|
|
16598
|
+
this.closed = false;
|
|
16599
|
+
this.wsUrl = wsUrl;
|
|
16600
|
+
this.getToken = getToken;
|
|
16601
|
+
}
|
|
16602
|
+
/**
|
|
16603
|
+
* Connect to the WebSocket server.
|
|
16604
|
+
* Resolves when connection is established, rejects on failure.
|
|
16605
|
+
*/
|
|
16606
|
+
async connect() {
|
|
16607
|
+
if (this.connected || this.connecting) return;
|
|
16608
|
+
this.connecting = true;
|
|
16609
|
+
const token = await this.getToken();
|
|
16610
|
+
if (!token) {
|
|
16611
|
+
this.connecting = false;
|
|
16612
|
+
throw new Error("No access token available for WebSocket connection");
|
|
16613
|
+
}
|
|
16614
|
+
return new Promise((resolve3, reject) => {
|
|
16615
|
+
logger.debug(`[WS] Connecting to ${this.wsUrl}...`);
|
|
16616
|
+
this.ws = new WebSocket(this.wsUrl, [`access_token.${token}`]);
|
|
16617
|
+
this.ws.onopen = () => {
|
|
16618
|
+
logger.debug("[WS] Connected");
|
|
16619
|
+
this.connected = true;
|
|
16620
|
+
this.connecting = false;
|
|
16621
|
+
this.reconnectAttempts = 0;
|
|
16622
|
+
this.startHeartbeat();
|
|
16623
|
+
resolve3();
|
|
16624
|
+
};
|
|
16625
|
+
this.ws.onmessage = (event) => {
|
|
16626
|
+
try {
|
|
16627
|
+
const data = typeof event.data === "string" ? event.data : event.data.toString();
|
|
16628
|
+
const message = JSON.parse(data);
|
|
16629
|
+
const requestId = message.requestId;
|
|
16630
|
+
if (requestId && this.handlers.has(requestId)) {
|
|
16631
|
+
this.handlers.get(requestId)(message);
|
|
16632
|
+
} else {
|
|
16633
|
+
logger.debug(`[WS] Unhandled message: ${message.action || "unknown"}`);
|
|
16634
|
+
}
|
|
16635
|
+
} catch (err) {
|
|
16636
|
+
logger.debug(`[WS] Failed to parse message: ${err}`);
|
|
16637
|
+
}
|
|
16638
|
+
};
|
|
16639
|
+
this.ws.onclose = () => {
|
|
16640
|
+
logger.debug("[WS] Connection closed");
|
|
16641
|
+
this.cleanup();
|
|
16642
|
+
this.notifyDisconnect();
|
|
16643
|
+
if (!this.closed) {
|
|
16644
|
+
this.scheduleReconnect();
|
|
16645
|
+
}
|
|
16646
|
+
};
|
|
16647
|
+
this.ws.onerror = (err) => {
|
|
16648
|
+
logger.debug(`[WS] Error: ${err}`);
|
|
16649
|
+
if (this.connecting) {
|
|
16650
|
+
this.connecting = false;
|
|
16651
|
+
this.connected = false;
|
|
16652
|
+
reject(new Error("WebSocket connection failed"));
|
|
16653
|
+
}
|
|
16654
|
+
};
|
|
16655
|
+
});
|
|
16656
|
+
}
|
|
16657
|
+
/** Whether the connection is currently established */
|
|
16658
|
+
get isConnected() {
|
|
16659
|
+
return this.connected;
|
|
16660
|
+
}
|
|
16661
|
+
/**
|
|
16662
|
+
* Send a JSON message over the WebSocket connection.
|
|
16663
|
+
*/
|
|
16664
|
+
send(data) {
|
|
16665
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
16666
|
+
throw new Error("WebSocket is not connected");
|
|
16667
|
+
}
|
|
16668
|
+
const payload = JSON.stringify(data);
|
|
16669
|
+
const sizeKB = (payload.length / 1024).toFixed(1);
|
|
16670
|
+
logger.debug(`[WS] Sending ${sizeKB} KB (action: ${data.action})`);
|
|
16671
|
+
if (payload.length > 32e3) {
|
|
16672
|
+
logger.warn(`[WS] Payload ${sizeKB} KB exceeds API Gateway 32 KB frame limit \u2014 connection will be closed`);
|
|
16673
|
+
}
|
|
16674
|
+
this.ws.send(payload);
|
|
16675
|
+
}
|
|
16676
|
+
/**
|
|
16677
|
+
* Register a handler for messages matching a specific requestId.
|
|
16678
|
+
*/
|
|
16679
|
+
onRequest(requestId, handler) {
|
|
16680
|
+
this.handlers.set(requestId, handler);
|
|
16681
|
+
}
|
|
16682
|
+
/**
|
|
16683
|
+
* Remove a handler for a specific requestId.
|
|
16684
|
+
*/
|
|
16685
|
+
offRequest(requestId) {
|
|
16686
|
+
this.handlers.delete(requestId);
|
|
16687
|
+
}
|
|
16688
|
+
/**
|
|
16689
|
+
* Register a handler that fires when the connection drops.
|
|
16690
|
+
*/
|
|
16691
|
+
onDisconnect(handler) {
|
|
16692
|
+
this.disconnectHandlers.add(handler);
|
|
16693
|
+
}
|
|
16694
|
+
/**
|
|
16695
|
+
* Remove a disconnect handler.
|
|
16696
|
+
*/
|
|
16697
|
+
offDisconnect(handler) {
|
|
16698
|
+
this.disconnectHandlers.delete(handler);
|
|
16699
|
+
}
|
|
16700
|
+
/**
|
|
16701
|
+
* Close the connection and stop all heartbeat/reconnect logic.
|
|
16702
|
+
*/
|
|
16703
|
+
disconnect() {
|
|
16704
|
+
this.closed = true;
|
|
16705
|
+
this.cleanup();
|
|
16706
|
+
if (this.ws) {
|
|
16707
|
+
this.ws.close();
|
|
16708
|
+
this.ws = null;
|
|
16709
|
+
}
|
|
16710
|
+
this.handlers.clear();
|
|
16711
|
+
this.disconnectHandlers.clear();
|
|
16712
|
+
}
|
|
16713
|
+
startHeartbeat() {
|
|
16714
|
+
this.stopHeartbeat();
|
|
16715
|
+
this.heartbeatInterval = setInterval(
|
|
16716
|
+
() => {
|
|
16717
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
16718
|
+
this.ws.send(JSON.stringify({ action: "heartbeat" }));
|
|
16719
|
+
logger.debug("[WS] Heartbeat sent");
|
|
16720
|
+
}
|
|
16721
|
+
},
|
|
16722
|
+
5 * 60 * 1e3
|
|
16723
|
+
);
|
|
16724
|
+
}
|
|
16725
|
+
stopHeartbeat() {
|
|
16726
|
+
if (this.heartbeatInterval) {
|
|
16727
|
+
clearInterval(this.heartbeatInterval);
|
|
16728
|
+
this.heartbeatInterval = null;
|
|
16729
|
+
}
|
|
16730
|
+
}
|
|
16731
|
+
cleanup() {
|
|
16732
|
+
this.connected = false;
|
|
16733
|
+
this.connecting = false;
|
|
16734
|
+
this.stopHeartbeat();
|
|
16735
|
+
if (this.reconnectTimer) {
|
|
16736
|
+
clearTimeout(this.reconnectTimer);
|
|
16737
|
+
this.reconnectTimer = null;
|
|
16738
|
+
}
|
|
16739
|
+
}
|
|
16740
|
+
notifyDisconnect() {
|
|
16741
|
+
for (const handler of this.disconnectHandlers) {
|
|
16742
|
+
try {
|
|
16743
|
+
handler();
|
|
16744
|
+
} catch {
|
|
16745
|
+
}
|
|
16746
|
+
}
|
|
16747
|
+
}
|
|
16748
|
+
scheduleReconnect() {
|
|
16749
|
+
if (this.closed) return;
|
|
16750
|
+
this.reconnectAttempts++;
|
|
16751
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts - 1), this.maxReconnectDelay);
|
|
16752
|
+
logger.debug(`[WS] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
16753
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
16754
|
+
this.reconnectTimer = null;
|
|
16755
|
+
if (this.closed) return;
|
|
16756
|
+
try {
|
|
16757
|
+
await this.connect();
|
|
16758
|
+
} catch {
|
|
16759
|
+
logger.debug("[WS] Reconnection failed");
|
|
16760
|
+
}
|
|
16761
|
+
}, delay);
|
|
16762
|
+
}
|
|
16763
|
+
};
|
|
16764
|
+
|
|
16765
|
+
// src/ws/WebSocketToolExecutor.ts
|
|
16766
|
+
import { v4 as uuidv412 } from "uuid";
|
|
16767
|
+
var WebSocketToolExecutor = class {
|
|
16768
|
+
constructor(wsManager, tokenGetter) {
|
|
16769
|
+
this.wsManager = wsManager;
|
|
16770
|
+
this.tokenGetter = tokenGetter;
|
|
16771
|
+
}
|
|
16772
|
+
/**
|
|
16773
|
+
* Execute a server-side tool via WebSocket.
|
|
16774
|
+
* Returns the tool result or throws on error.
|
|
16775
|
+
*/
|
|
16776
|
+
async execute(toolName, input, abortSignal) {
|
|
16777
|
+
if (!this.wsManager.isConnected) {
|
|
16778
|
+
throw new Error("WebSocket is not connected");
|
|
16779
|
+
}
|
|
16780
|
+
const token = await this.tokenGetter();
|
|
16781
|
+
if (!token) {
|
|
16782
|
+
throw new Error("No access token available");
|
|
16783
|
+
}
|
|
16784
|
+
const requestId = uuidv412();
|
|
16785
|
+
return new Promise((resolve3, reject) => {
|
|
16786
|
+
let settled = false;
|
|
16787
|
+
const settle = (action) => {
|
|
16788
|
+
if (settled) return;
|
|
16789
|
+
settled = true;
|
|
16790
|
+
this.wsManager.offRequest(requestId);
|
|
16791
|
+
this.wsManager.offDisconnect(onDisconnect);
|
|
16792
|
+
action();
|
|
16793
|
+
};
|
|
16794
|
+
const settleResolve = (result) => settle(() => resolve3(result));
|
|
16795
|
+
const settleReject = (err) => settle(() => reject(err));
|
|
16796
|
+
const onDisconnect = () => {
|
|
16797
|
+
settleReject(new Error("WebSocket connection lost during tool execution"));
|
|
16798
|
+
};
|
|
16799
|
+
this.wsManager.onDisconnect(onDisconnect);
|
|
16800
|
+
if (abortSignal) {
|
|
16801
|
+
if (abortSignal.aborted) {
|
|
16802
|
+
settleReject(new Error("Tool execution aborted"));
|
|
16803
|
+
return;
|
|
16804
|
+
}
|
|
16805
|
+
abortSignal.addEventListener("abort", () => settleReject(new Error("Tool execution aborted")), {
|
|
16806
|
+
once: true
|
|
16807
|
+
});
|
|
16808
|
+
}
|
|
16809
|
+
this.wsManager.onRequest(requestId, (message) => {
|
|
16810
|
+
if (message.action === "cli_tool_response") {
|
|
16811
|
+
settleResolve({
|
|
16812
|
+
success: message.success,
|
|
16813
|
+
content: message.content,
|
|
16814
|
+
error: message.error
|
|
16815
|
+
});
|
|
16816
|
+
}
|
|
16817
|
+
});
|
|
16818
|
+
try {
|
|
16819
|
+
this.wsManager.send({
|
|
16820
|
+
action: "cli_tool_request",
|
|
16821
|
+
accessToken: token,
|
|
16822
|
+
requestId,
|
|
16823
|
+
toolName,
|
|
16824
|
+
input
|
|
16825
|
+
});
|
|
16826
|
+
} catch (err) {
|
|
16827
|
+
settleReject(err instanceof Error ? err : new Error(String(err)));
|
|
16828
|
+
}
|
|
16829
|
+
});
|
|
16830
|
+
}
|
|
16831
|
+
};
|
|
16832
|
+
|
|
16011
16833
|
// src/auth/ApiClient.ts
|
|
16012
16834
|
import axios11 from "axios";
|
|
16013
16835
|
var ApiClient = class {
|
|
@@ -17913,7 +18735,8 @@ function CliApp() {
|
|
|
17913
18735
|
agentStore: null,
|
|
17914
18736
|
abortController: null,
|
|
17915
18737
|
contextContent: "",
|
|
17916
|
-
backgroundManager: null
|
|
18738
|
+
backgroundManager: null,
|
|
18739
|
+
wsManager: null
|
|
17917
18740
|
});
|
|
17918
18741
|
const [isInitialized, setIsInitialized] = useState10(false);
|
|
17919
18742
|
const [initError, setInitError] = useState10(null);
|
|
@@ -17941,6 +18764,10 @@ function CliApp() {
|
|
|
17941
18764
|
})
|
|
17942
18765
|
);
|
|
17943
18766
|
}
|
|
18767
|
+
if (state.wsManager) {
|
|
18768
|
+
state.wsManager.disconnect();
|
|
18769
|
+
setWebSocketToolExecutor(null);
|
|
18770
|
+
}
|
|
17944
18771
|
if (state.agent) {
|
|
17945
18772
|
state.agent.removeAllListeners();
|
|
17946
18773
|
}
|
|
@@ -17959,7 +18786,7 @@ function CliApp() {
|
|
|
17959
18786
|
setTimeout(() => {
|
|
17960
18787
|
process.exit(0);
|
|
17961
18788
|
}, 100);
|
|
17962
|
-
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore]);
|
|
18789
|
+
}, [state.session, state.sessionStore, state.mcpManager, state.agent, state.imageStore, state.wsManager]);
|
|
17963
18790
|
useInput9((input, key) => {
|
|
17964
18791
|
if (key.escape) {
|
|
17965
18792
|
const store = useCliStore.getState();
|
|
@@ -18034,7 +18861,7 @@ function CliApp() {
|
|
|
18034
18861
|
if (!isAuthenticated) {
|
|
18035
18862
|
console.log("\u2139\uFE0F AI features disabled. Available commands: /login, /help, /config\n");
|
|
18036
18863
|
const minimalSession = {
|
|
18037
|
-
id:
|
|
18864
|
+
id: uuidv413(),
|
|
18038
18865
|
name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
18039
18866
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18040
18867
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -18062,10 +18889,45 @@ function CliApp() {
|
|
|
18062
18889
|
console.log(`\u{1F30D} API Environment: ${envName} (${apiBaseURL})`);
|
|
18063
18890
|
}
|
|
18064
18891
|
const apiClient = new ApiClient(apiBaseURL, state.configStore);
|
|
18065
|
-
const
|
|
18066
|
-
|
|
18067
|
-
|
|
18068
|
-
}
|
|
18892
|
+
const tokenGetter = async () => {
|
|
18893
|
+
const tokens = await state.configStore.getAuthTokens();
|
|
18894
|
+
return tokens?.accessToken ?? null;
|
|
18895
|
+
};
|
|
18896
|
+
let wsManager = null;
|
|
18897
|
+
let llm;
|
|
18898
|
+
try {
|
|
18899
|
+
const serverConfig = await apiClient.get(
|
|
18900
|
+
"/api/settings/serverConfig"
|
|
18901
|
+
);
|
|
18902
|
+
const wsUrl = serverConfig?.websocketUrl;
|
|
18903
|
+
const wsCompletionUrl = serverConfig?.wsCompletionUrl;
|
|
18904
|
+
if (wsUrl && wsCompletionUrl) {
|
|
18905
|
+
wsManager = new WebSocketConnectionManager(wsUrl, tokenGetter);
|
|
18906
|
+
await wsManager.connect();
|
|
18907
|
+
const wsToolExecutor2 = new WebSocketToolExecutor(wsManager, tokenGetter);
|
|
18908
|
+
setWebSocketToolExecutor(wsToolExecutor2);
|
|
18909
|
+
llm = new WebSocketLlmBackend({
|
|
18910
|
+
wsManager,
|
|
18911
|
+
apiClient,
|
|
18912
|
+
model: config.defaultModel,
|
|
18913
|
+
tokenGetter,
|
|
18914
|
+
wsCompletionUrl
|
|
18915
|
+
});
|
|
18916
|
+
logger.debug("\u{1F50C} Using WebSocket transport (bypasses CloudFront timeout)");
|
|
18917
|
+
} else {
|
|
18918
|
+
throw new Error("No websocketUrl or wsCompletionUrl in server config");
|
|
18919
|
+
}
|
|
18920
|
+
} catch (wsError) {
|
|
18921
|
+
logger.debug(
|
|
18922
|
+
`[WS] WebSocket unavailable, using SSE fallback: ${wsError instanceof Error ? wsError.message : String(wsError)}`
|
|
18923
|
+
);
|
|
18924
|
+
wsManager = null;
|
|
18925
|
+
setWebSocketToolExecutor(null);
|
|
18926
|
+
llm = new ServerLlmBackend({
|
|
18927
|
+
apiClient,
|
|
18928
|
+
model: config.defaultModel
|
|
18929
|
+
});
|
|
18930
|
+
}
|
|
18069
18931
|
const models = await llm.getModelInfo();
|
|
18070
18932
|
if (models.length === 0) {
|
|
18071
18933
|
throw new Error("No models available from server.");
|
|
@@ -18078,7 +18940,7 @@ function CliApp() {
|
|
|
18078
18940
|
}
|
|
18079
18941
|
llm.currentModel = modelInfo.id;
|
|
18080
18942
|
const newSession = {
|
|
18081
|
-
id:
|
|
18943
|
+
id: uuidv413(),
|
|
18082
18944
|
name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
18083
18945
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18084
18946
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -18292,8 +19154,10 @@ function CliApp() {
|
|
|
18292
19154
|
// Store agent store for agent management commands
|
|
18293
19155
|
contextContent: contextResult.mergedContent,
|
|
18294
19156
|
// Store raw context for compact instructions
|
|
18295
|
-
backgroundManager
|
|
19157
|
+
backgroundManager,
|
|
18296
19158
|
// Store for grouped notification turn tracking
|
|
19159
|
+
wsManager
|
|
19160
|
+
// WebSocket connection manager (null if using SSE fallback)
|
|
18297
19161
|
}));
|
|
18298
19162
|
setStoreSession(newSession);
|
|
18299
19163
|
const bannerLines = [
|
|
@@ -18388,13 +19252,13 @@ function CliApp() {
|
|
|
18388
19252
|
messageContent = multimodalMessage.content;
|
|
18389
19253
|
}
|
|
18390
19254
|
const userMessage = {
|
|
18391
|
-
id:
|
|
19255
|
+
id: uuidv413(),
|
|
18392
19256
|
role: "user",
|
|
18393
19257
|
content: userMessageContent,
|
|
18394
19258
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
18395
19259
|
};
|
|
18396
19260
|
const pendingAssistantMessage = {
|
|
18397
|
-
id:
|
|
19261
|
+
id: uuidv413(),
|
|
18398
19262
|
role: "assistant",
|
|
18399
19263
|
content: "...",
|
|
18400
19264
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -18607,13 +19471,13 @@ function CliApp() {
|
|
|
18607
19471
|
userMessageContent = message;
|
|
18608
19472
|
}
|
|
18609
19473
|
const userMessage = {
|
|
18610
|
-
id:
|
|
19474
|
+
id: uuidv413(),
|
|
18611
19475
|
role: "user",
|
|
18612
19476
|
content: userMessageContent,
|
|
18613
19477
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
18614
19478
|
};
|
|
18615
19479
|
const pendingAssistantMessage = {
|
|
18616
|
-
id:
|
|
19480
|
+
id: uuidv413(),
|
|
18617
19481
|
role: "assistant",
|
|
18618
19482
|
content: "...",
|
|
18619
19483
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -18693,7 +19557,7 @@ function CliApp() {
|
|
|
18693
19557
|
const currentSession = useCliStore.getState().session;
|
|
18694
19558
|
if (currentSession) {
|
|
18695
19559
|
const cancelMessage = {
|
|
18696
|
-
id:
|
|
19560
|
+
id: uuidv413(),
|
|
18697
19561
|
role: "assistant",
|
|
18698
19562
|
content: "\u26A0\uFE0F Operation cancelled by user",
|
|
18699
19563
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -18738,7 +19602,7 @@ function CliApp() {
|
|
|
18738
19602
|
setState((prev) => ({ ...prev, abortController }));
|
|
18739
19603
|
try {
|
|
18740
19604
|
const pendingAssistantMessage = {
|
|
18741
|
-
id:
|
|
19605
|
+
id: uuidv413(),
|
|
18742
19606
|
role: "assistant",
|
|
18743
19607
|
content: "...",
|
|
18744
19608
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -18766,7 +19630,7 @@ function CliApp() {
|
|
|
18766
19630
|
const currentSession = useCliStore.getState().session;
|
|
18767
19631
|
if (!currentSession) return;
|
|
18768
19632
|
const continuationMessage = {
|
|
18769
|
-
id:
|
|
19633
|
+
id: uuidv413(),
|
|
18770
19634
|
role: "assistant",
|
|
18771
19635
|
content: "---\n\n**Background Agent Results:**\n\n" + result.finalAnswer,
|
|
18772
19636
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -18827,13 +19691,13 @@ function CliApp() {
|
|
|
18827
19691
|
isError = true;
|
|
18828
19692
|
}
|
|
18829
19693
|
const userMessage = {
|
|
18830
|
-
id:
|
|
19694
|
+
id: uuidv413(),
|
|
18831
19695
|
role: "user",
|
|
18832
19696
|
content: `$ ${command}`,
|
|
18833
19697
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
18834
19698
|
};
|
|
18835
19699
|
const assistantMessage = {
|
|
18836
|
-
id:
|
|
19700
|
+
id: uuidv413(),
|
|
18837
19701
|
role: "assistant",
|
|
18838
19702
|
content: isError ? `\u274C Error:
|
|
18839
19703
|
${output}` : output.trim() || "(no output)",
|
|
@@ -19302,7 +20166,7 @@ Keyboard Shortcuts:
|
|
|
19302
20166
|
console.clear();
|
|
19303
20167
|
const model = state.session?.model || state.config?.defaultModel || "claude-sonnet";
|
|
19304
20168
|
const newSession = {
|
|
19305
|
-
id:
|
|
20169
|
+
id: uuidv413(),
|
|
19306
20170
|
name: `Session ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
19307
20171
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19308
20172
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -19807,9 +20671,9 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
19807
20671
|
return { ...prev, config: updatedConfig };
|
|
19808
20672
|
});
|
|
19809
20673
|
if (modelChanged && state.agent) {
|
|
19810
|
-
const
|
|
19811
|
-
if (
|
|
19812
|
-
|
|
20674
|
+
const backend = state.agent.context.llm;
|
|
20675
|
+
if (backend) {
|
|
20676
|
+
backend.currentModel = updatedConfig.defaultModel;
|
|
19813
20677
|
}
|
|
19814
20678
|
}
|
|
19815
20679
|
};
|