jinzd-ai-cli 0.4.155 → 0.4.157
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/{batch-LS3IJVBK.js → batch-ATL6V3UE.js} +2 -2
- package/dist/{chat-index-IF4EINLQ.js → chat-index-2I7ZHRE5.js} +2 -2
- package/dist/{chunk-E5ICQT3P.js → chunk-3HJW556L.js} +4 -4
- package/dist/{chunk-JOJRBV2K.js → chunk-62CD2T5F.js} +1 -1
- package/dist/{chunk-D6GJTJQH.js → chunk-77DDRYFM.js} +1 -1
- package/dist/{chunk-CIZQZ7CC.js → chunk-A4JROOGF.js} +2 -2
- package/dist/{chunk-NFRTSL3N.js → chunk-GRFQ2QD5.js} +1 -1
- package/dist/{chunk-JXSWY54M.js → chunk-LLQMVGNP.js} +1 -1
- package/dist/{chunk-B3LFGPU2.js → chunk-N3VGZTEJ.js} +1 -1
- package/dist/chunk-NZ4X6GUC.js +230 -0
- package/dist/{chunk-O6MLS5QO.js → chunk-OJL3PY36.js} +0 -226
- package/dist/{hub-ZILVZWI2.js → chunk-Q3ZUDA6S.js} +6 -249
- package/dist/{persist-3EBOLHFZ.js → chunk-RUJQ5OUB.js} +1 -2
- package/dist/{chunk-IBBYW6PM.js → chunk-V5OQOKU3.js} +1 -1
- package/dist/{ci-34ZQH43L.js → ci-E7MDZSB6.js} +3 -3
- package/dist/{constants-DQ5VJOGS.js → constants-NL5ETRA5.js} +1 -1
- package/dist/{doctor-cli-TSCI4ORL.js → doctor-cli-XJNT745O.js} +6 -6
- package/dist/electron-server.js +758 -44
- package/dist/hub-RXZ5IDBY.js +260 -0
- package/dist/{hub-server-OH7AYQIW.js → hub-server-GSTG5MNE.js} +4 -2
- package/dist/index.js +40 -40
- package/dist/persist-UI6WRBGB.js +12 -0
- package/dist/{run-tests-5KWCHBQS.js → run-tests-I3EBH7O6.js} +2 -2
- package/dist/{run-tests-5CJRMOMI.js → run-tests-O46AI32W.js} +1 -1
- package/dist/{server-35OQV62B.js → server-4PJJHZWM.js} +142 -32
- package/dist/{server-DVIP7NLW.js → server-SVBHOHTC.js} +6 -6
- package/dist/{task-orchestrator-AXSS7ROD.js → task-orchestrator-NMX3CYW2.js} +6 -6
- package/dist/web/client/app.js +173 -0
- package/dist/web/client/index.html +31 -0
- package/package.json +1 -1
- package/dist/{chunk-U5MY24UZ.js → chunk-MM3F43H6.js} +3 -3
package/dist/electron-server.js
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
VERSION,
|
|
37
37
|
buildUserIdentityPrompt,
|
|
38
38
|
runTestsTool
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-LLQMVGNP.js";
|
|
40
40
|
import {
|
|
41
41
|
hasSemanticIndex,
|
|
42
42
|
semanticSearch
|
|
@@ -57,7 +57,7 @@ import "./chunk-3RG5ZIWI.js";
|
|
|
57
57
|
import express from "express";
|
|
58
58
|
import { createServer } from "http";
|
|
59
59
|
import { WebSocketServer } from "ws";
|
|
60
|
-
import { join as
|
|
60
|
+
import { join as join17, dirname as dirname6, resolve as resolve6, relative as relative3, sep as sep3 } from "path";
|
|
61
61
|
import { existsSync as existsSync23, readFileSync as readFileSync16, readdirSync as readdirSync11, statSync as statSync9, realpathSync } from "fs";
|
|
62
62
|
import { networkInterfaces } from "os";
|
|
63
63
|
|
|
@@ -10662,7 +10662,7 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
10662
10662
|
|
|
10663
10663
|
// src/web/session-handler.ts
|
|
10664
10664
|
import { existsSync as existsSync21, readFileSync as readFileSync14, appendFileSync as appendFileSync3, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, readdirSync as readdirSync9, statSync as statSync8, createWriteStream, unlinkSync as unlinkSync4 } from "fs";
|
|
10665
|
-
import { join as
|
|
10665
|
+
import { join as join15, resolve as resolve5, dirname as dirname5 } from "path";
|
|
10666
10666
|
import { execSync as execSync3 } from "child_process";
|
|
10667
10667
|
|
|
10668
10668
|
// src/tools/git-context.ts
|
|
@@ -10755,6 +10755,619 @@ function formatGitContextForPrompt(ctx) {
|
|
|
10755
10755
|
return lines.join("\n");
|
|
10756
10756
|
}
|
|
10757
10757
|
|
|
10758
|
+
// src/hub/convergence.ts
|
|
10759
|
+
function convergenceThreshold(total) {
|
|
10760
|
+
if (total <= 0) return Infinity;
|
|
10761
|
+
return Math.ceil(total * 2 / 3);
|
|
10762
|
+
}
|
|
10763
|
+
function isConverged(convergedCount, total) {
|
|
10764
|
+
if (convergedCount <= 0) return false;
|
|
10765
|
+
return convergedCount >= convergenceThreshold(total);
|
|
10766
|
+
}
|
|
10767
|
+
var CONVERGED_MARKER = /\[CONVERGED\]/i;
|
|
10768
|
+
function hasConvergedMarker(content) {
|
|
10769
|
+
return CONVERGED_MARKER.test(content);
|
|
10770
|
+
}
|
|
10771
|
+
function stripConvergedMarker(content) {
|
|
10772
|
+
return content.replace(/\s*\[CONVERGED\]\s*/gi, " ").trim();
|
|
10773
|
+
}
|
|
10774
|
+
|
|
10775
|
+
// src/hub/agent.ts
|
|
10776
|
+
var PASS_MARKER = "[PASS]";
|
|
10777
|
+
function parseTurn(raw) {
|
|
10778
|
+
const trimmed = raw.trim();
|
|
10779
|
+
const upper = trimmed.toUpperCase();
|
|
10780
|
+
if (upper.startsWith(PASS_MARKER) || upper === PASS_MARKER) {
|
|
10781
|
+
return { content: "", passed: true, converged: false };
|
|
10782
|
+
}
|
|
10783
|
+
const converged = hasConvergedMarker(trimmed);
|
|
10784
|
+
return { content: converged ? stripConvergedMarker(trimmed) : trimmed, passed: false, converged };
|
|
10785
|
+
}
|
|
10786
|
+
var HubAgent = class {
|
|
10787
|
+
role;
|
|
10788
|
+
providers;
|
|
10789
|
+
defaultProvider;
|
|
10790
|
+
defaultModel;
|
|
10791
|
+
/** External context documents injected into system prompt */
|
|
10792
|
+
context;
|
|
10793
|
+
constructor(role, providers, defaultProvider, defaultModel, context) {
|
|
10794
|
+
this.role = role;
|
|
10795
|
+
this.providers = providers;
|
|
10796
|
+
this.defaultProvider = defaultProvider;
|
|
10797
|
+
this.defaultModel = defaultModel;
|
|
10798
|
+
this.context = context;
|
|
10799
|
+
}
|
|
10800
|
+
get providerId() {
|
|
10801
|
+
return this.role.provider ?? this.defaultProvider;
|
|
10802
|
+
}
|
|
10803
|
+
get modelId() {
|
|
10804
|
+
return this.role.model ?? this.defaultModel;
|
|
10805
|
+
}
|
|
10806
|
+
/**
|
|
10807
|
+
* Generate this agent's response given the full discussion history.
|
|
10808
|
+
*
|
|
10809
|
+
* Returns a DiscussionMessage, with `passed: true` if the agent outputs [PASS].
|
|
10810
|
+
*/
|
|
10811
|
+
async speak(topic, history, round, maxRounds, opts) {
|
|
10812
|
+
const provider = this.providers.get(this.providerId);
|
|
10813
|
+
if (!provider) {
|
|
10814
|
+
throw new Error(`Provider "${this.providerId}" not available for agent "${this.role.id}"`);
|
|
10815
|
+
}
|
|
10816
|
+
const systemPrompt = this.buildSystemPrompt(topic, round, maxRounds, opts?.voteConverge);
|
|
10817
|
+
const messages = this.buildMessages(history);
|
|
10818
|
+
const response = await provider.chat({
|
|
10819
|
+
messages,
|
|
10820
|
+
model: this.modelId,
|
|
10821
|
+
systemPrompt,
|
|
10822
|
+
stream: false,
|
|
10823
|
+
temperature: 0.7,
|
|
10824
|
+
maxTokens: 4096
|
|
10825
|
+
});
|
|
10826
|
+
const { content, passed, converged } = parseTurn(response.content);
|
|
10827
|
+
return {
|
|
10828
|
+
speaker: this.role.id,
|
|
10829
|
+
speakerName: this.role.name,
|
|
10830
|
+
content,
|
|
10831
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
10832
|
+
passed,
|
|
10833
|
+
converged
|
|
10834
|
+
};
|
|
10835
|
+
}
|
|
10836
|
+
/**
|
|
10837
|
+
* Streaming version of speak() — yields tokens as they arrive.
|
|
10838
|
+
* Falls back to non-streaming speak() if the provider doesn't support chatStream.
|
|
10839
|
+
*/
|
|
10840
|
+
async speakStream(topic, history, round, maxRounds, onToken, opts) {
|
|
10841
|
+
const provider = this.providers.get(this.providerId);
|
|
10842
|
+
if (!provider) {
|
|
10843
|
+
throw new Error(`Provider "${this.providerId}" not available for agent "${this.role.id}"`);
|
|
10844
|
+
}
|
|
10845
|
+
if (!provider.chatStream) {
|
|
10846
|
+
return this.speak(topic, history, round, maxRounds, opts);
|
|
10847
|
+
}
|
|
10848
|
+
const systemPrompt = this.buildSystemPrompt(topic, round, maxRounds, opts?.voteConverge);
|
|
10849
|
+
const messages = this.buildMessages(history);
|
|
10850
|
+
let raw = "";
|
|
10851
|
+
const stream = provider.chatStream({
|
|
10852
|
+
messages,
|
|
10853
|
+
model: this.modelId,
|
|
10854
|
+
systemPrompt,
|
|
10855
|
+
stream: true,
|
|
10856
|
+
temperature: 0.7,
|
|
10857
|
+
maxTokens: 4096
|
|
10858
|
+
});
|
|
10859
|
+
for await (const chunk of stream) {
|
|
10860
|
+
if (chunk.delta) {
|
|
10861
|
+
raw += chunk.delta;
|
|
10862
|
+
onToken?.(chunk.delta);
|
|
10863
|
+
}
|
|
10864
|
+
}
|
|
10865
|
+
const { content, passed, converged } = parseTurn(raw);
|
|
10866
|
+
return {
|
|
10867
|
+
speaker: this.role.id,
|
|
10868
|
+
speakerName: this.role.name,
|
|
10869
|
+
content,
|
|
10870
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
10871
|
+
passed,
|
|
10872
|
+
converged
|
|
10873
|
+
};
|
|
10874
|
+
}
|
|
10875
|
+
/**
|
|
10876
|
+
* Generate a summary of the entire discussion from this agent's perspective.
|
|
10877
|
+
*/
|
|
10878
|
+
async summarize(topic, history) {
|
|
10879
|
+
const provider = this.providers.get(this.providerId);
|
|
10880
|
+
if (!provider) {
|
|
10881
|
+
throw new Error(`Provider "${this.providerId}" not available`);
|
|
10882
|
+
}
|
|
10883
|
+
const messages = [
|
|
10884
|
+
{
|
|
10885
|
+
role: "user",
|
|
10886
|
+
content: this.buildSummaryPrompt(topic, history),
|
|
10887
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
10888
|
+
}
|
|
10889
|
+
];
|
|
10890
|
+
const response = await provider.chat({
|
|
10891
|
+
messages,
|
|
10892
|
+
model: this.modelId,
|
|
10893
|
+
stream: false,
|
|
10894
|
+
temperature: 0.3,
|
|
10895
|
+
maxTokens: 4096
|
|
10896
|
+
});
|
|
10897
|
+
return response.content.trim();
|
|
10898
|
+
}
|
|
10899
|
+
// ── Private ──────────────────────────────────────────────────────
|
|
10900
|
+
buildSystemPrompt(topic, round, maxRounds, voteConverge) {
|
|
10901
|
+
const contextSection = this.context ? `
|
|
10902
|
+
|
|
10903
|
+
## Reference Documents
|
|
10904
|
+
${this.context}` : "";
|
|
10905
|
+
const convergeRule = voteConverge ? `
|
|
10906
|
+
- If you believe the group has reached a sufficient conclusion and further rounds would add little, append the marker [CONVERGED] at the very end of your message (you may still make your substantive point above it). When a 2/3 majority converges, the discussion ends.` : "";
|
|
10907
|
+
return `# Multi-Agent Discussion \u2014 Role: ${this.role.name}
|
|
10908
|
+
|
|
10909
|
+
${this.role.persona}
|
|
10910
|
+
|
|
10911
|
+
## Discussion Rules
|
|
10912
|
+
- You are participating in a multi-agent discussion about the topic below.
|
|
10913
|
+
- You can see what other participants have said. Build on their ideas, challenge them, or add your own perspective.
|
|
10914
|
+
- Stay in character as ${this.role.name}. Respond from your role's expertise and viewpoint.
|
|
10915
|
+
- Keep responses concise and focused (2-6 paragraphs). Do not repeat what others have already said.
|
|
10916
|
+
- If you have nothing meaningful to add (others have covered your points), respond with exactly: [PASS]${convergeRule}
|
|
10917
|
+
- This is round ${round} of ${maxRounds}. ${round >= maxRounds - 1 ? "This is one of the final rounds \u2014 try to converge on conclusions." : ""}
|
|
10918
|
+
- Use the language that the topic is written in (if the topic is in Chinese, respond in Chinese).
|
|
10919
|
+
|
|
10920
|
+
## Topic
|
|
10921
|
+
${topic}${contextSection}`;
|
|
10922
|
+
}
|
|
10923
|
+
buildMessages(history) {
|
|
10924
|
+
const messages = [];
|
|
10925
|
+
for (const msg of history) {
|
|
10926
|
+
if (msg.passed) continue;
|
|
10927
|
+
if (msg.speaker === this.role.id) {
|
|
10928
|
+
messages.push({
|
|
10929
|
+
role: "assistant",
|
|
10930
|
+
content: msg.content,
|
|
10931
|
+
timestamp: msg.timestamp
|
|
10932
|
+
});
|
|
10933
|
+
} else {
|
|
10934
|
+
const prefix = `[${msg.speakerName} (${msg.speaker})]:`;
|
|
10935
|
+
messages.push({
|
|
10936
|
+
role: "user",
|
|
10937
|
+
content: `${prefix}
|
|
10938
|
+
${msg.content}`,
|
|
10939
|
+
timestamp: msg.timestamp
|
|
10940
|
+
});
|
|
10941
|
+
}
|
|
10942
|
+
}
|
|
10943
|
+
if (messages.length === 0) {
|
|
10944
|
+
messages.push({
|
|
10945
|
+
role: "user",
|
|
10946
|
+
content: "Please share your initial thoughts on the topic described in the system prompt. You are the first to speak.",
|
|
10947
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
10948
|
+
});
|
|
10949
|
+
} else {
|
|
10950
|
+
messages.push({
|
|
10951
|
+
role: "user",
|
|
10952
|
+
content: "It is now your turn to respond. Consider what the other participants have said and share your perspective. If you have nothing to add, respond with [PASS].",
|
|
10953
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
10954
|
+
});
|
|
10955
|
+
}
|
|
10956
|
+
return messages;
|
|
10957
|
+
}
|
|
10958
|
+
buildSummaryPrompt(topic, history) {
|
|
10959
|
+
const transcript = history.filter((m) => !m.passed).map((m) => `**${m.speakerName}** (${m.speaker}):
|
|
10960
|
+
${m.content}`).join("\n\n---\n\n");
|
|
10961
|
+
return `Please synthesize the following multi-agent discussion into a clear, structured summary.
|
|
10962
|
+
|
|
10963
|
+
## Discussion Topic
|
|
10964
|
+
${topic}
|
|
10965
|
+
|
|
10966
|
+
## Full Transcript
|
|
10967
|
+
${transcript}
|
|
10968
|
+
|
|
10969
|
+
## Instructions
|
|
10970
|
+
Produce a structured, decision-oriented synthesis with these exact sections:
|
|
10971
|
+
|
|
10972
|
+
1. **Decision / Recommendation** \u2014 the single clearest recommendation the discussion points to. If the group genuinely did not converge, say so and give the leading option plus what would settle it.
|
|
10973
|
+
2. **Key Points of Agreement** \u2014 what participants agreed on.
|
|
10974
|
+
3. **Points of Debate** \u2014 where they disagreed, with the competing perspectives.
|
|
10975
|
+
4. **Action Items** \u2014 concrete next steps as a checklist. For each, suggest which role/expertise should own it, e.g. "- [ ] (\u67B6\u6784\u5E08) \u8BC4\u4F30\u670D\u52A1\u62C6\u5206\u8FB9\u754C".
|
|
10976
|
+
5. **Risks & Open Questions** \u2014 unresolved issues and what to watch.
|
|
10977
|
+
|
|
10978
|
+
Keep it tight (within ~600 words). Use the same language as the discussion.`;
|
|
10979
|
+
}
|
|
10980
|
+
};
|
|
10981
|
+
|
|
10982
|
+
// src/hub/discuss.ts
|
|
10983
|
+
var DiscussionOrchestrator = class {
|
|
10984
|
+
agents = [];
|
|
10985
|
+
state;
|
|
10986
|
+
aborted = false;
|
|
10987
|
+
humanSteer;
|
|
10988
|
+
voteConverge;
|
|
10989
|
+
/** Callback for rendering events */
|
|
10990
|
+
onEvent;
|
|
10991
|
+
/**
|
|
10992
|
+
* P2: human-in-the-loop. When `config.humanSteer` is on, the orchestrator
|
|
10993
|
+
* awaits this between rounds so a person can inject guidance or stop. The
|
|
10994
|
+
* CLI layer supplies the actual prompt; the orchestrator stays UI-agnostic.
|
|
10995
|
+
*/
|
|
10996
|
+
onRoundReview;
|
|
10997
|
+
constructor(config, providers) {
|
|
10998
|
+
this.agents = config.roles.map(
|
|
10999
|
+
(role) => new HubAgent(role, providers, config.defaultProvider, config.defaultModel, config.context)
|
|
11000
|
+
);
|
|
11001
|
+
this.humanSteer = config.humanSteer ?? false;
|
|
11002
|
+
this.voteConverge = config.voteConverge ?? false;
|
|
11003
|
+
this.state = {
|
|
11004
|
+
topic: "",
|
|
11005
|
+
messages: [],
|
|
11006
|
+
round: 0,
|
|
11007
|
+
maxRounds: config.maxRounds ?? 10,
|
|
11008
|
+
finished: false
|
|
11009
|
+
};
|
|
11010
|
+
}
|
|
11011
|
+
/** Get all agents */
|
|
11012
|
+
getAgents() {
|
|
11013
|
+
return this.agents;
|
|
11014
|
+
}
|
|
11015
|
+
/** Current discussion state (used to persist even after an error). */
|
|
11016
|
+
getState() {
|
|
11017
|
+
return this.state;
|
|
11018
|
+
}
|
|
11019
|
+
/** Signal the orchestrator to stop after current turn */
|
|
11020
|
+
abort() {
|
|
11021
|
+
this.aborted = true;
|
|
11022
|
+
}
|
|
11023
|
+
/**
|
|
11024
|
+
* Run the full discussion on a given topic.
|
|
11025
|
+
* Returns the final DiscussionState including summary.
|
|
11026
|
+
*/
|
|
11027
|
+
async run(topic) {
|
|
11028
|
+
this.state.topic = topic;
|
|
11029
|
+
this.state.round = 0;
|
|
11030
|
+
this.state.finished = false;
|
|
11031
|
+
this.aborted = false;
|
|
11032
|
+
try {
|
|
11033
|
+
for (let round = 1; round <= this.state.maxRounds; round++) {
|
|
11034
|
+
if (this.aborted) {
|
|
11035
|
+
this.emit({ type: "discussion_end", reason: "user_interrupt" });
|
|
11036
|
+
break;
|
|
11037
|
+
}
|
|
11038
|
+
this.state.round = round;
|
|
11039
|
+
this.emit({ type: "round_start", round, maxRounds: this.state.maxRounds });
|
|
11040
|
+
let allPassed = true;
|
|
11041
|
+
let convergedCount = 0;
|
|
11042
|
+
for (const agent of this.agents) {
|
|
11043
|
+
if (this.aborted) break;
|
|
11044
|
+
this.emit({ type: "agent_speaking", roleId: agent.role.id, roleName: agent.role.name });
|
|
11045
|
+
try {
|
|
11046
|
+
const message = await agent.speakStream(
|
|
11047
|
+
topic,
|
|
11048
|
+
this.state.messages,
|
|
11049
|
+
round,
|
|
11050
|
+
this.state.maxRounds,
|
|
11051
|
+
(token) => this.emit({ type: "agent_token", roleId: agent.role.id, token }),
|
|
11052
|
+
{ voteConverge: this.voteConverge }
|
|
11053
|
+
);
|
|
11054
|
+
this.state.messages.push(message);
|
|
11055
|
+
if (message.converged) convergedCount++;
|
|
11056
|
+
if (message.passed) {
|
|
11057
|
+
this.emit({ type: "agent_passed", roleId: agent.role.id });
|
|
11058
|
+
this.emit({ type: "agent_spoke", roleId: agent.role.id, message });
|
|
11059
|
+
} else {
|
|
11060
|
+
allPassed = false;
|
|
11061
|
+
this.emit({ type: "agent_spoke", roleId: agent.role.id, message });
|
|
11062
|
+
}
|
|
11063
|
+
} catch (err) {
|
|
11064
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
11065
|
+
this.emit({ type: "error", roleId: agent.role.id, message: errMsg });
|
|
11066
|
+
this.state.messages.push({
|
|
11067
|
+
speaker: "system",
|
|
11068
|
+
speakerName: "System",
|
|
11069
|
+
content: `[${agent.role.name} encountered an error: ${errMsg}]`,
|
|
11070
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
11071
|
+
});
|
|
11072
|
+
}
|
|
11073
|
+
}
|
|
11074
|
+
this.emit({ type: "round_end", round });
|
|
11075
|
+
if (allPassed && round > 1) {
|
|
11076
|
+
this.emit({ type: "discussion_end", reason: "consensus" });
|
|
11077
|
+
break;
|
|
11078
|
+
}
|
|
11079
|
+
if (this.voteConverge && convergedCount > 0) {
|
|
11080
|
+
this.emit({ type: "converge_vote", converged: convergedCount, total: this.agents.length });
|
|
11081
|
+
if (isConverged(convergedCount, this.agents.length)) {
|
|
11082
|
+
this.emit({ type: "discussion_end", reason: "vote_converged" });
|
|
11083
|
+
break;
|
|
11084
|
+
}
|
|
11085
|
+
}
|
|
11086
|
+
if (round === this.state.maxRounds) {
|
|
11087
|
+
this.emit({ type: "discussion_end", reason: "max_rounds", maxRounds: this.state.maxRounds });
|
|
11088
|
+
break;
|
|
11089
|
+
}
|
|
11090
|
+
if (this.humanSteer && this.onRoundReview && !this.aborted) {
|
|
11091
|
+
const steer = await this.onRoundReview({ round, maxRounds: this.state.maxRounds, state: this.state });
|
|
11092
|
+
if (steer.action === "stop") {
|
|
11093
|
+
this.emit({ type: "discussion_end", reason: "human_stop" });
|
|
11094
|
+
break;
|
|
11095
|
+
}
|
|
11096
|
+
const guidance = steer.message?.trim();
|
|
11097
|
+
if (guidance) {
|
|
11098
|
+
this.state.messages.push({
|
|
11099
|
+
speaker: "human",
|
|
11100
|
+
speakerName: "\u4E3B\u6301\u4EBA (You)",
|
|
11101
|
+
content: guidance,
|
|
11102
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
11103
|
+
});
|
|
11104
|
+
this.emit({ type: "human_steer", message: guidance });
|
|
11105
|
+
}
|
|
11106
|
+
}
|
|
11107
|
+
}
|
|
11108
|
+
} catch (err) {
|
|
11109
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
11110
|
+
this.emit({ type: "error", message: errMsg });
|
|
11111
|
+
}
|
|
11112
|
+
await this.generateSummary();
|
|
11113
|
+
this.state.finished = true;
|
|
11114
|
+
return this.state;
|
|
11115
|
+
}
|
|
11116
|
+
// ── Private ──────────────────────────────────────────────────────
|
|
11117
|
+
async generateSummary() {
|
|
11118
|
+
if (this.state.messages.filter((m) => !m.passed && m.speaker !== "system").length === 0) {
|
|
11119
|
+
this.state.summary = "(No substantive discussion occurred.)";
|
|
11120
|
+
this.emit({ type: "summary", content: this.state.summary });
|
|
11121
|
+
return;
|
|
11122
|
+
}
|
|
11123
|
+
const summarizer = this.agents[0];
|
|
11124
|
+
if (!summarizer) {
|
|
11125
|
+
this.state.summary = "(No agents available to summarize.)";
|
|
11126
|
+
this.emit({ type: "summary", content: this.state.summary });
|
|
11127
|
+
return;
|
|
11128
|
+
}
|
|
11129
|
+
try {
|
|
11130
|
+
this.state.summary = await summarizer.summarize(this.state.topic, this.state.messages);
|
|
11131
|
+
this.emit({ type: "summary", content: this.state.summary });
|
|
11132
|
+
} catch (err) {
|
|
11133
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
11134
|
+
this.state.summary = `(Summary generation failed: ${errMsg})`;
|
|
11135
|
+
this.emit({ type: "summary", content: this.state.summary });
|
|
11136
|
+
}
|
|
11137
|
+
}
|
|
11138
|
+
emit(event) {
|
|
11139
|
+
this.onEvent?.(event);
|
|
11140
|
+
}
|
|
11141
|
+
};
|
|
11142
|
+
|
|
11143
|
+
// src/hub/presets.ts
|
|
11144
|
+
var PRESETS = [
|
|
11145
|
+
{
|
|
11146
|
+
id: "tech-review",
|
|
11147
|
+
name: "Tech Review Panel",
|
|
11148
|
+
description: "\u67B6\u6784\u5E08 + \u5F00\u53D1\u8005 + \u5B89\u5168\u4E13\u5BB6 \u8BA8\u8BBA\u6280\u672F\u65B9\u6848",
|
|
11149
|
+
roles: [
|
|
11150
|
+
{
|
|
11151
|
+
id: "architect",
|
|
11152
|
+
name: "\u67B6\u6784\u5E08",
|
|
11153
|
+
persona: `\u4F60\u662F\u4E00\u4F4D\u8D44\u6DF1\u8F6F\u4EF6\u67B6\u6784\u5E08\uFF0C\u6709 15 \u5E74\u4EE5\u4E0A\u7684\u7CFB\u7EDF\u8BBE\u8BA1\u7ECF\u9A8C\u3002
|
|
11154
|
+
\u4F60\u7684\u4E13\u957F\uFF1A\u7CFB\u7EDF\u67B6\u6784\u3001\u53EF\u6269\u5C55\u6027\u3001\u6280\u672F\u9009\u578B\u3001\u8BBE\u8BA1\u6A21\u5F0F\u3002
|
|
11155
|
+
\u4F60\u5173\u6CE8\u7684\u7EF4\u5EA6\uFF1A\u7CFB\u7EDF\u6574\u4F53\u7ED3\u6784\u3001\u6A21\u5757\u89E3\u8026\u3001\u957F\u671F\u53EF\u7EF4\u62A4\u6027\u3001\u6280\u672F\u503A\u52A1\u3002
|
|
11156
|
+
\u98CE\u683C\uFF1A\u5168\u5C40\u89C6\u89D2\uFF0C\u5584\u4E8E\u6743\u8861 trade-off\uFF0C\u7528\u56FE\u8868\u548C\u7C7B\u6BD4\u89E3\u91CA\u590D\u6742\u6982\u5FF5\u3002`,
|
|
11157
|
+
color: "cyan"
|
|
11158
|
+
},
|
|
11159
|
+
{
|
|
11160
|
+
id: "developer",
|
|
11161
|
+
name: "\u5168\u6808\u5F00\u53D1\u8005",
|
|
11162
|
+
persona: `\u4F60\u662F\u4E00\u4F4D\u7ECF\u9A8C\u4E30\u5BCC\u7684\u5168\u6808\u5F00\u53D1\u8005\uFF0C\u7CBE\u901A\u524D\u7AEF\u548C\u540E\u7AEF\u6280\u672F\u6808\u3002
|
|
11163
|
+
\u4F60\u7684\u4E13\u957F\uFF1A\u4EE3\u7801\u5B9E\u73B0\u3001\u6027\u80FD\u4F18\u5316\u3001API \u8BBE\u8BA1\u3001\u5F00\u53D1\u6548\u7387\u3002
|
|
11164
|
+
\u4F60\u5173\u6CE8\u7684\u7EF4\u5EA6\uFF1A\u4EE3\u7801\u53EF\u8BFB\u6027\u3001\u5B9E\u73B0\u590D\u6742\u5EA6\u3001DX(\u5F00\u53D1\u8005\u4F53\u9A8C)\u3001\u5177\u4F53\u6280\u672F\u7EC6\u8282\u3002
|
|
11165
|
+
\u98CE\u683C\uFF1A\u52A1\u5B9E\u3001\u6CE8\u91CD\u7EC6\u8282\uFF0C\u559C\u6B22\u7ED9\u51FA\u5177\u4F53\u7684\u4EE3\u7801\u793A\u4F8B\u548C\u5B9E\u73B0\u5EFA\u8BAE\u3002`,
|
|
11166
|
+
color: "green"
|
|
11167
|
+
},
|
|
11168
|
+
{
|
|
11169
|
+
id: "security",
|
|
11170
|
+
name: "\u5B89\u5168\u4E13\u5BB6",
|
|
11171
|
+
persona: `\u4F60\u662F\u4E00\u4F4D\u7F51\u7EDC\u5B89\u5168\u4E13\u5BB6\uFF0C\u4E13\u6CE8\u4E8E\u5E94\u7528\u5B89\u5168\u548C\u5B89\u5168\u67B6\u6784\u8BBE\u8BA1\u3002
|
|
11172
|
+
\u4F60\u7684\u4E13\u957F\uFF1A\u5A01\u80C1\u5EFA\u6A21\u3001\u6F0F\u6D1E\u5206\u6790\u3001\u5B89\u5168\u6700\u4F73\u5B9E\u8DF5\u3001\u5408\u89C4\u8981\u6C42\u3002
|
|
11173
|
+
\u4F60\u5173\u6CE8\u7684\u7EF4\u5EA6\uFF1A\u653B\u51FB\u9762\u3001\u6570\u636E\u4FDD\u62A4\u3001\u8BA4\u8BC1\u6388\u6743\u3001\u5B89\u5168\u7F16\u7801\u5B9E\u8DF5\u3002
|
|
11174
|
+
\u98CE\u683C\uFF1A\u8C28\u614E\u3001\u4E25\u8C28\uFF0C\u5584\u4E8E\u53D1\u73B0\u6F5C\u5728\u98CE\u9669\uFF0C\u540C\u65F6\u4E5F\u4F1A\u63D0\u51FA\u5B9E\u7528\u7684\u5B89\u5168\u65B9\u6848\u800C\u4E0D\u662F\u4E00\u5473\u5426\u5B9A\u3002`,
|
|
11175
|
+
color: "red"
|
|
11176
|
+
}
|
|
11177
|
+
]
|
|
11178
|
+
},
|
|
11179
|
+
{
|
|
11180
|
+
id: "brainstorm",
|
|
11181
|
+
name: "Brainstorm Team",
|
|
11182
|
+
description: "\u521B\u610F\u8005 + \u5206\u6790\u5E08 + \u6267\u884C\u8005 \u5934\u8111\u98CE\u66B4",
|
|
11183
|
+
roles: [
|
|
11184
|
+
{
|
|
11185
|
+
id: "creative",
|
|
11186
|
+
name: "\u521B\u610F\u8005",
|
|
11187
|
+
persona: `\u4F60\u662F\u4E00\u4F4D\u5145\u6EE1\u521B\u610F\u7684\u4EA7\u54C1\u601D\u8003\u8005\uFF0C\u5584\u4E8E\u8DF3\u51FA\u6846\u67B6\u601D\u8003\u3002
|
|
11188
|
+
\u4F60\u7684\u4E13\u957F\uFF1A\u521B\u65B0\u601D\u7EF4\u3001\u7528\u6237\u4F53\u9A8C\u3001\u4EA7\u54C1\u613F\u666F\u3001\u8BBE\u8BA1\u601D\u7EF4\u3002
|
|
11189
|
+
\u4F60\u5173\u6CE8\u7684\u7EF4\u5EA6\uFF1A\u7528\u6237\u9700\u6C42\u3001\u521B\u65B0\u6027\u3001\u5DEE\u5F02\u5316\u3001\u60C5\u611F\u4EF7\u503C\u3002
|
|
11190
|
+
\u98CE\u683C\uFF1A\u53D1\u6563\u6027\u601D\u7EF4\uFF0C\u5927\u80C6\u63D0\u51FA\u65B0\u60F3\u6CD5\uFF0C\u4E0D\u6015\u5929\u9A6C\u884C\u7A7A\u3002\u5584\u4E8E\u7528\u6545\u4E8B\u548C\u573A\u666F\u6765\u63CF\u8FF0\u6784\u60F3\u3002`,
|
|
11191
|
+
color: "magenta"
|
|
11192
|
+
},
|
|
11193
|
+
{
|
|
11194
|
+
id: "analyst",
|
|
11195
|
+
name: "\u5206\u6790\u5E08",
|
|
11196
|
+
persona: `\u4F60\u662F\u4E00\u4F4D\u7406\u6027\u7684\u6570\u636E\u5206\u6790\u5E08\uFF0C\u5584\u4E8E\u7528\u6570\u636E\u548C\u903B\u8F91\u8BC4\u4F30\u65B9\u6848\u3002
|
|
11197
|
+
\u4F60\u7684\u4E13\u957F\uFF1A\u6570\u636E\u5206\u6790\u3001\u5E02\u573A\u7814\u7A76\u3001\u53EF\u884C\u6027\u8BC4\u4F30\u3001ROI \u5206\u6790\u3002
|
|
11198
|
+
\u4F60\u5173\u6CE8\u7684\u7EF4\u5EA6\uFF1A\u6570\u636E\u652F\u6491\u3001\u6210\u672C\u6536\u76CA\u3001\u98CE\u9669\u8BC4\u4F30\u3001\u5E02\u573A\u53EF\u884C\u6027\u3002
|
|
11199
|
+
\u98CE\u683C\uFF1A\u4E25\u8C28\u5BA2\u89C2\uFF0C\u5584\u4E8E\u63D0\u51FA\u5173\u952E\u95EE\u9898\uFF0C\u7528\u6570\u636E\u8BF4\u8BDD\u3002\u4E0D\u8F7B\u6613\u5426\u5B9A\u4F46\u4F1A\u6307\u51FA\u903B\u8F91\u6F0F\u6D1E\u3002`,
|
|
11200
|
+
color: "yellow"
|
|
11201
|
+
},
|
|
11202
|
+
{
|
|
11203
|
+
id: "executor",
|
|
11204
|
+
name: "\u6267\u884C\u8005",
|
|
11205
|
+
persona: `\u4F60\u662F\u4E00\u4F4D\u9AD8\u6548\u7684\u9879\u76EE\u6267\u884C\u8005\uFF0C\u5584\u4E8E\u5C06\u60F3\u6CD5\u8F6C\u5316\u4E3A\u53EF\u6267\u884C\u7684\u8BA1\u5212\u3002
|
|
11206
|
+
\u4F60\u7684\u4E13\u957F\uFF1A\u9879\u76EE\u7BA1\u7406\u3001\u8D44\u6E90\u89C4\u5212\u3001\u98CE\u9669\u7BA1\u7406\u3001\u654F\u6377\u65B9\u6CD5\u3002
|
|
11207
|
+
\u4F60\u5173\u6CE8\u7684\u7EF4\u5EA6\uFF1A\u53EF\u6267\u884C\u6027\u3001\u65F6\u95F4\u7EBF\u3001\u8D44\u6E90\u9700\u6C42\u3001MVP \u7B56\u7565\u3002
|
|
11208
|
+
\u98CE\u683C\uFF1A\u5B9E\u9645\u3001\u9AD8\u6548\uFF0C\u5584\u4E8E\u62C6\u89E3\u4EFB\u52A1\uFF0C\u63D0\u51FA\u5177\u4F53\u7684\u5B9E\u65BD\u6B65\u9AA4\u548C\u91CC\u7A0B\u7891\u3002`,
|
|
11209
|
+
color: "green"
|
|
11210
|
+
}
|
|
11211
|
+
]
|
|
11212
|
+
},
|
|
11213
|
+
{
|
|
11214
|
+
id: "code-review",
|
|
11215
|
+
name: "Code Review Panel",
|
|
11216
|
+
description: "\u4EE3\u7801\u8D28\u91CF + \u6027\u80FD + \u53EF\u6D4B\u8BD5\u6027 \u591A\u89D2\u5EA6\u5BA1\u67E5",
|
|
11217
|
+
roles: [
|
|
11218
|
+
{
|
|
11219
|
+
id: "quality",
|
|
11220
|
+
name: "\u4EE3\u7801\u8D28\u91CF\u5BA1\u67E5\u5458",
|
|
11221
|
+
persona: `\u4F60\u662F\u4E00\u4F4D\u4EE3\u7801\u8D28\u91CF\u4E13\u5BB6\uFF0C\u4E13\u6CE8\u4E8E\u53EF\u8BFB\u6027\u3001\u53EF\u7EF4\u62A4\u6027\u548C\u6700\u4F73\u5B9E\u8DF5\u3002
|
|
11222
|
+
\u4F60\u7684\u4E13\u957F\uFF1AClean Code\u3001\u8BBE\u8BA1\u6A21\u5F0F\u3001SOLID \u539F\u5219\u3001\u91CD\u6784\u6280\u672F\u3002
|
|
11223
|
+
\u4F60\u5173\u6CE8\u7684\u7EF4\u5EA6\uFF1A\u547D\u540D\u89C4\u8303\u3001\u51FD\u6570\u957F\u5EA6\u3001\u8026\u5408\u5EA6\u3001\u4EE3\u7801\u590D\u6742\u5EA6\u3001\u6CE8\u91CA\u8D28\u91CF\u3002
|
|
11224
|
+
\u98CE\u683C\uFF1A\u5EFA\u8BBE\u6027\u6279\u8BC4\uFF0C\u603B\u662F\u7ED9\u51FA\u6539\u8FDB\u5EFA\u8BAE\u800C\u4E0D\u4EC5\u662F\u6307\u51FA\u95EE\u9898\u3002`,
|
|
11225
|
+
color: "cyan"
|
|
11226
|
+
},
|
|
11227
|
+
{
|
|
11228
|
+
id: "perf",
|
|
11229
|
+
name: "\u6027\u80FD\u5DE5\u7A0B\u5E08",
|
|
11230
|
+
persona: `\u4F60\u662F\u4E00\u4F4D\u6027\u80FD\u4F18\u5316\u4E13\u5BB6\uFF0C\u5584\u4E8E\u53D1\u73B0\u548C\u89E3\u51B3\u6027\u80FD\u74F6\u9888\u3002
|
|
11231
|
+
\u4F60\u7684\u4E13\u957F\uFF1A\u7B97\u6CD5\u590D\u6742\u5EA6\u3001\u5185\u5B58\u7BA1\u7406\u3001\u5E76\u53D1\u4F18\u5316\u3001\u7F13\u5B58\u7B56\u7565\u3002
|
|
11232
|
+
\u4F60\u5173\u6CE8\u7684\u7EF4\u5EA6\uFF1A\u65F6\u95F4\u590D\u6742\u5EA6\u3001\u7A7A\u95F4\u590D\u6742\u5EA6\u3001IO \u74F6\u9888\u3001\u70ED\u70B9\u8DEF\u5F84\u3002
|
|
11233
|
+
\u98CE\u683C\uFF1A\u6570\u636E\u9A71\u52A8\uFF0C\u559C\u6B22\u7528\u57FA\u51C6\u6D4B\u8BD5\u548C\u5927 O \u5206\u6790\u6765\u8BBA\u8BC1\u89C2\u70B9\u3002`,
|
|
11234
|
+
color: "yellow"
|
|
11235
|
+
},
|
|
11236
|
+
{
|
|
11237
|
+
id: "testing",
|
|
11238
|
+
name: "\u6D4B\u8BD5\u67B6\u6784\u5E08",
|
|
11239
|
+
persona: `\u4F60\u662F\u4E00\u4F4D\u6D4B\u8BD5\u67B6\u6784\u5E08\uFF0C\u4E13\u6CE8\u4E8E\u4EE3\u7801\u7684\u53EF\u6D4B\u8BD5\u6027\u548C\u6D4B\u8BD5\u7B56\u7565\u3002
|
|
11240
|
+
\u4F60\u7684\u4E13\u957F\uFF1A\u5355\u5143\u6D4B\u8BD5\u3001\u96C6\u6210\u6D4B\u8BD5\u3001Mock \u7B56\u7565\u3001TDD\u3001\u6D4B\u8BD5\u8986\u76D6\u7387\u3002
|
|
11241
|
+
\u4F60\u5173\u6CE8\u7684\u7EF4\u5EA6\uFF1A\u53EF\u6D4B\u8BD5\u6027\u3001\u8FB9\u754C\u6761\u4EF6\u3001\u9519\u8BEF\u5904\u7406\u8DEF\u5F84\u3001\u6D4B\u8BD5\u91D1\u5B57\u5854\u3002
|
|
11242
|
+
\u98CE\u683C\uFF1A\u5173\u6CE8\u8FB9\u754C\u60C5\u51B5\u548C\u5F02\u5E38\u8DEF\u5F84\uFF0C\u5584\u4E8E\u63D0\u51FA"\u5982\u679C...\u600E\u4E48\u529E"\u7684\u95EE\u9898\u3002`,
|
|
11243
|
+
color: "green"
|
|
11244
|
+
}
|
|
11245
|
+
]
|
|
11246
|
+
},
|
|
11247
|
+
{
|
|
11248
|
+
id: "debate",
|
|
11249
|
+
name: "Debate (Pro vs Con)",
|
|
11250
|
+
description: "\u6B63\u65B9 + \u53CD\u65B9 + \u4E3B\u6301\u4EBA \u8FA9\u8BBA\u6A21\u5F0F",
|
|
11251
|
+
roles: [
|
|
11252
|
+
{
|
|
11253
|
+
id: "pro",
|
|
11254
|
+
name: "\u6B63\u65B9",
|
|
11255
|
+
persona: `\u4F60\u662F\u8FA9\u8BBA\u4E2D\u7684\u6B63\u65B9\uFF0C\u4F60\u7684\u4EFB\u52A1\u662F\u8BBA\u8BC1\u8BA8\u8BBA\u4E3B\u9898\u7684\u6B63\u9762\u4EF7\u503C\u3002
|
|
11256
|
+
\u4F60\u5FC5\u987B\uFF1A\u4E3A\u4E3B\u9898\u8FA9\u62A4\uFF0C\u627E\u5230\u652F\u6301\u8BBA\u636E\uFF0C\u56DE\u5E94\u53CD\u65B9\u7684\u8D28\u7591\u3002
|
|
11257
|
+
\u98CE\u683C\uFF1A\u903B\u8F91\u4E25\u5BC6\uFF0C\u5584\u4E8E\u5F15\u7528\u6848\u4F8B\u548C\u6570\u636E\u3002\u5373\u4F7F\u9762\u5BF9\u5F3A\u6709\u529B\u7684\u53CD\u9A73\u4E5F\u8981\u627E\u5230\u65B0\u7684\u8BBA\u8BC1\u89D2\u5EA6\u3002
|
|
11258
|
+
\u6CE8\u610F\uFF1A\u4F60\u53EF\u4EE5\u627F\u8BA4\u5BF9\u65B9\u7684\u90E8\u5206\u89C2\u70B9\uFF0C\u4F46\u8981\u6307\u51FA\u8FD9\u4E0D\u5F71\u54CD\u4F60\u7684\u6838\u5FC3\u8BBA\u70B9\u3002`,
|
|
11259
|
+
color: "green"
|
|
11260
|
+
},
|
|
11261
|
+
{
|
|
11262
|
+
id: "con",
|
|
11263
|
+
name: "\u53CD\u65B9",
|
|
11264
|
+
persona: `\u4F60\u662F\u8FA9\u8BBA\u4E2D\u7684\u53CD\u65B9\uFF0C\u4F60\u7684\u4EFB\u52A1\u662F\u627E\u51FA\u8BA8\u8BBA\u4E3B\u9898\u7684\u95EE\u9898\u548C\u98CE\u9669\u3002
|
|
11265
|
+
\u4F60\u5FC5\u987B\uFF1A\u63D0\u51FA\u8D28\u7591\uFF0C\u53D1\u73B0\u6F0F\u6D1E\uFF0C\u6307\u51FA\u6F5C\u5728\u98CE\u9669\u548C\u66FF\u4EE3\u65B9\u6848\u3002
|
|
11266
|
+
\u98CE\u683C\uFF1A\u7280\u5229\u3001\u5584\u4E8E\u53CD\u95EE\uFF0C\u7528\u53CD\u9762\u6848\u4F8B\u548C\u903B\u8F91\u63A8\u7406\u6765\u8BBA\u8BC1\u3002
|
|
11267
|
+
\u6CE8\u610F\uFF1A\u4F60\u4E0D\u662F\u4E3A\u4E86\u5426\u5B9A\u800C\u5426\u5B9A\uFF0C\u800C\u662F\u901A\u8FC7\u8D28\u7591\u6765\u5E2E\u52A9\u5168\u9762\u7406\u89E3\u95EE\u9898\u3002`,
|
|
11268
|
+
color: "red"
|
|
11269
|
+
},
|
|
11270
|
+
{
|
|
11271
|
+
id: "moderator",
|
|
11272
|
+
name: "\u4E3B\u6301\u4EBA",
|
|
11273
|
+
persona: `\u4F60\u662F\u8FA9\u8BBA\u7684\u4E3B\u6301\u4EBA\uFF0C\u4F60\u7684\u4EFB\u52A1\u662F\u5F15\u5BFC\u8BA8\u8BBA\u3001\u603B\u7ED3\u89C2\u70B9\u3001\u63D0\u51FA\u65B0\u7684\u8BA8\u8BBA\u65B9\u5411\u3002
|
|
11274
|
+
\u4F60\u5FC5\u987B\uFF1A\u4FDD\u6301\u4E2D\u7ACB\uFF0C\u603B\u7ED3\u53CC\u65B9\u8981\u70B9\uFF0C\u5728\u8BA8\u8BBA\u9677\u5165\u50F5\u5C40\u65F6\u63D0\u51FA\u65B0\u89D2\u5EA6\u3002
|
|
11275
|
+
\u98CE\u683C\uFF1A\u516C\u6B63\u3001\u5584\u4E8E\u5F52\u7EB3\uFF0C\u6BCF\u6B21\u53D1\u8A00\u5148\u7B80\u77ED\u603B\u7ED3\u53CC\u65B9\u89C2\u70B9\uFF0C\u518D\u5F15\u5BFC\u4E0B\u4E00\u4E2A\u8BA8\u8BBA\u65B9\u5411\u3002
|
|
11276
|
+
\u6CE8\u610F\uFF1A\u4E0D\u8981\u8FC7\u591A\u53D1\u8868\u4E2A\u4EBA\u89C2\u70B9\uFF0C\u91CD\u70B9\u662F\u63A8\u8FDB\u8BA8\u8BBA\u8D28\u91CF\u3002`,
|
|
11277
|
+
color: "cyan"
|
|
11278
|
+
}
|
|
11279
|
+
]
|
|
11280
|
+
}
|
|
11281
|
+
];
|
|
11282
|
+
function getPreset(id) {
|
|
11283
|
+
return PRESETS.find((p) => p.id === id);
|
|
11284
|
+
}
|
|
11285
|
+
|
|
11286
|
+
// src/hub/resolve-providers.ts
|
|
11287
|
+
function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, available, mix) {
|
|
11288
|
+
const warnings = [];
|
|
11289
|
+
let pool = null;
|
|
11290
|
+
if (mix !== void 0 && mix !== false) {
|
|
11291
|
+
if (typeof mix === "string" && mix.trim().length > 0) {
|
|
11292
|
+
const requested = mix.split(",").map((s) => s.trim()).filter(Boolean);
|
|
11293
|
+
pool = [];
|
|
11294
|
+
for (const id of requested) {
|
|
11295
|
+
if (lookup.has(id)) pool.push(id);
|
|
11296
|
+
else warnings.push(`--mix: provider "${id}" not configured \u2014 skipped.`);
|
|
11297
|
+
}
|
|
11298
|
+
} else {
|
|
11299
|
+
pool = [...available];
|
|
11300
|
+
}
|
|
11301
|
+
if (pool.length === 0) {
|
|
11302
|
+
warnings.push(`--mix: no usable providers \u2014 all roles fall back to "${defaultProvider}".`);
|
|
11303
|
+
pool = [defaultProvider];
|
|
11304
|
+
}
|
|
11305
|
+
}
|
|
11306
|
+
const providerDefaultModel = (pid) => pid === defaultProvider ? defaultModel : lookup.defaultModelFor(pid) ?? defaultModel;
|
|
11307
|
+
const assignments = [];
|
|
11308
|
+
const outRoles = roles.map((role, i) => {
|
|
11309
|
+
const desired = pool ? pool[i % pool.length] : role.provider;
|
|
11310
|
+
let providerId;
|
|
11311
|
+
let fellBack = false;
|
|
11312
|
+
if (desired && lookup.has(desired)) {
|
|
11313
|
+
providerId = desired;
|
|
11314
|
+
} else {
|
|
11315
|
+
if (desired && !lookup.has(desired)) {
|
|
11316
|
+
warnings.push(`role "${role.id}": provider "${desired}" not available \u2014 using "${defaultProvider}".`);
|
|
11317
|
+
fellBack = true;
|
|
11318
|
+
}
|
|
11319
|
+
providerId = defaultProvider;
|
|
11320
|
+
}
|
|
11321
|
+
const explicitProvider = role.provider ?? defaultProvider;
|
|
11322
|
+
const modelId = !pool && role.model && explicitProvider === providerId ? role.model : providerDefaultModel(providerId);
|
|
11323
|
+
assignments.push({ roleId: role.id, roleName: role.name, providerId, modelId, fellBack });
|
|
11324
|
+
return { ...role, provider: providerId, model: modelId };
|
|
11325
|
+
});
|
|
11326
|
+
return { roles: outRoles, assignments, warnings };
|
|
11327
|
+
}
|
|
11328
|
+
|
|
11329
|
+
// src/hub/persist.ts
|
|
11330
|
+
import { join as join14 } from "path";
|
|
11331
|
+
function discussionToMessages(state2) {
|
|
11332
|
+
const out = [];
|
|
11333
|
+
const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
|
|
11334
|
+
out.push({ role: "user", content: `\u{1F3DB} Topic: ${state2.topic}`, timestamp: t0 });
|
|
11335
|
+
for (const m of state2.messages) {
|
|
11336
|
+
if (m.speaker === "system") {
|
|
11337
|
+
out.push({ role: "system", content: m.content, timestamp: m.timestamp });
|
|
11338
|
+
continue;
|
|
11339
|
+
}
|
|
11340
|
+
if (m.speaker === "human") {
|
|
11341
|
+
out.push({ role: "user", content: `\u{1F9ED} ${m.speakerName}: ${m.content}`, timestamp: m.timestamp });
|
|
11342
|
+
continue;
|
|
11343
|
+
}
|
|
11344
|
+
if (m.passed || !m.content.trim()) continue;
|
|
11345
|
+
const tag = m.converged ? " \u2713converged" : "";
|
|
11346
|
+
out.push({
|
|
11347
|
+
role: "assistant",
|
|
11348
|
+
content: `**${m.speakerName}** (${m.speaker})${tag}
|
|
11349
|
+
|
|
11350
|
+
${m.content}`,
|
|
11351
|
+
timestamp: m.timestamp
|
|
11352
|
+
});
|
|
11353
|
+
}
|
|
11354
|
+
if (state2.summary && state2.summary.trim()) {
|
|
11355
|
+
out.push({ role: "assistant", content: `\u{1F4CB} **Summary**
|
|
11356
|
+
|
|
11357
|
+
${state2.summary}`, timestamp: /* @__PURE__ */ new Date() });
|
|
11358
|
+
}
|
|
11359
|
+
return out;
|
|
11360
|
+
}
|
|
11361
|
+
async function persistDiscussion(state2, config, defaultProvider, defaultModel) {
|
|
11362
|
+
const sm = new SessionManager(config);
|
|
11363
|
+
const session = sm.createSession(defaultProvider, defaultModel);
|
|
11364
|
+
session.messages = discussionToMessages(state2);
|
|
11365
|
+
session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
|
|
11366
|
+
session.titleAiGenerated = true;
|
|
11367
|
+
await sm.save();
|
|
11368
|
+
return { id: session.id, path: join14(config.getHistoryDir(), `${session.id}.json`) };
|
|
11369
|
+
}
|
|
11370
|
+
|
|
10758
11371
|
// src/web/session-handler.ts
|
|
10759
11372
|
var FREE_ROUND_TOOLS = /* @__PURE__ */ new Set(["write_todos"]);
|
|
10760
11373
|
var MAX_CONSECUTIVE_FREE_ROUNDS = 5;
|
|
@@ -10785,6 +11398,10 @@ var SessionHandler = class _SessionHandler {
|
|
|
10785
11398
|
abortController = null;
|
|
10786
11399
|
userInterjection = null;
|
|
10787
11400
|
processing = false;
|
|
11401
|
+
/** P4: active hub discussion orchestrator (web room), if any. */
|
|
11402
|
+
hubOrchestrator = null;
|
|
11403
|
+
/** P4b: pending between-rounds human-steer prompts (requestId → resolver). */
|
|
11404
|
+
pendingHubReview = /* @__PURE__ */ new Map();
|
|
10788
11405
|
/** Pending ask_user promises */
|
|
10789
11406
|
pendingAskUser = /* @__PURE__ */ new Map();
|
|
10790
11407
|
/** Pending auto-pause promises */
|
|
@@ -10923,6 +11540,19 @@ var SessionHandler = class _SessionHandler {
|
|
|
10923
11540
|
this.abortController.abort();
|
|
10924
11541
|
}
|
|
10925
11542
|
return;
|
|
11543
|
+
case "hub_start":
|
|
11544
|
+
return this.handleHubStart(msg);
|
|
11545
|
+
case "hub_abort":
|
|
11546
|
+
this.hubOrchestrator?.abort();
|
|
11547
|
+
return;
|
|
11548
|
+
case "hub_steer": {
|
|
11549
|
+
const resolve7 = this.pendingHubReview.get(msg.requestId);
|
|
11550
|
+
if (resolve7) {
|
|
11551
|
+
this.pendingHubReview.delete(msg.requestId);
|
|
11552
|
+
resolve7({ action: msg.action, message: msg.message });
|
|
11553
|
+
}
|
|
11554
|
+
return;
|
|
11555
|
+
}
|
|
10926
11556
|
case "interjection":
|
|
10927
11557
|
this.userInterjection = msg.content;
|
|
10928
11558
|
return;
|
|
@@ -11013,6 +11643,90 @@ var SessionHandler = class _SessionHandler {
|
|
|
11013
11643
|
}
|
|
11014
11644
|
}
|
|
11015
11645
|
// ── Chat handling ────────────────────────────────────────────────
|
|
11646
|
+
/**
|
|
11647
|
+
* P4: run a multi-agent hub discussion in the browser room. Bridges the
|
|
11648
|
+
* UI-agnostic DiscussionOrchestrator (onEvent) to WebSocket hub_event
|
|
11649
|
+
* messages, then persists the result (P3) so it can be replayed.
|
|
11650
|
+
*/
|
|
11651
|
+
async handleHubStart(msg) {
|
|
11652
|
+
if (this.processing || this.hubOrchestrator) {
|
|
11653
|
+
this.send({ type: "error", message: "Already running a request. Abort first." });
|
|
11654
|
+
return;
|
|
11655
|
+
}
|
|
11656
|
+
const topic = (msg.topic ?? "").trim();
|
|
11657
|
+
if (!topic) {
|
|
11658
|
+
this.send({ type: "error", message: "Hub topic is required." });
|
|
11659
|
+
return;
|
|
11660
|
+
}
|
|
11661
|
+
const preset = getPreset(msg.preset ?? "brainstorm");
|
|
11662
|
+
if (!preset) {
|
|
11663
|
+
this.send({ type: "error", message: `Unknown preset "${msg.preset}".` });
|
|
11664
|
+
return;
|
|
11665
|
+
}
|
|
11666
|
+
const defaultProvider = this.currentProvider;
|
|
11667
|
+
const defaultModel = this.currentModel;
|
|
11668
|
+
const resolution = resolveRoleProviders(
|
|
11669
|
+
preset.roles,
|
|
11670
|
+
{
|
|
11671
|
+
has: (id) => this.providers.has(id),
|
|
11672
|
+
defaultModelFor: (id) => this.providers.has(id) ? this.providers.get(id).info.defaultModel : void 0
|
|
11673
|
+
},
|
|
11674
|
+
defaultProvider,
|
|
11675
|
+
defaultModel,
|
|
11676
|
+
this.providers.listAvailable().map((p) => p.info.id),
|
|
11677
|
+
msg.mix
|
|
11678
|
+
);
|
|
11679
|
+
const roles = resolution.roles;
|
|
11680
|
+
for (const w of resolution.warnings) this.send({ type: "info", message: w });
|
|
11681
|
+
const config = {
|
|
11682
|
+
mode: "discuss",
|
|
11683
|
+
roles,
|
|
11684
|
+
defaultProvider,
|
|
11685
|
+
defaultModel,
|
|
11686
|
+
maxRounds: msg.maxRounds && msg.maxRounds > 0 ? Math.min(msg.maxRounds, 20) : 6,
|
|
11687
|
+
voteConverge: msg.vote === true,
|
|
11688
|
+
humanSteer: msg.steer === true
|
|
11689
|
+
};
|
|
11690
|
+
this.send({
|
|
11691
|
+
type: "hub_started",
|
|
11692
|
+
topic,
|
|
11693
|
+
maxRounds: config.maxRounds,
|
|
11694
|
+
roles: roles.map((r) => ({ id: r.id, name: r.name, color: r.color, provider: r.provider, model: r.model }))
|
|
11695
|
+
});
|
|
11696
|
+
const orchestrator = new DiscussionOrchestrator(config, this.providers);
|
|
11697
|
+
this.hubOrchestrator = orchestrator;
|
|
11698
|
+
orchestrator.onEvent = (event) => this.send({ type: "hub_event", event });
|
|
11699
|
+
if (config.humanSteer) {
|
|
11700
|
+
orchestrator.onRoundReview = ({ round, maxRounds }) => new Promise((resolve7) => {
|
|
11701
|
+
const requestId = `hubrev_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
11702
|
+
this.pendingHubReview.set(requestId, resolve7);
|
|
11703
|
+
this.send({ type: "hub_review", requestId, round, maxRounds });
|
|
11704
|
+
});
|
|
11705
|
+
}
|
|
11706
|
+
this.processing = true;
|
|
11707
|
+
try {
|
|
11708
|
+
const state2 = await orchestrator.run(topic);
|
|
11709
|
+
let sessionId;
|
|
11710
|
+
let saved = false;
|
|
11711
|
+
if (state2.messages.length > 0) {
|
|
11712
|
+
try {
|
|
11713
|
+
const res = await persistDiscussion(state2, this.config, defaultProvider, defaultModel);
|
|
11714
|
+
sessionId = res.id;
|
|
11715
|
+
saved = true;
|
|
11716
|
+
} catch (err) {
|
|
11717
|
+
this.send({ type: "info", message: `Could not save discussion: ${err.message}` });
|
|
11718
|
+
}
|
|
11719
|
+
}
|
|
11720
|
+
this.send({ type: "hub_done", sessionId, saved });
|
|
11721
|
+
if (saved) this.sendSessionList();
|
|
11722
|
+
} catch (err) {
|
|
11723
|
+
this.send({ type: "error", message: `Hub error: ${err.message}` });
|
|
11724
|
+
this.send({ type: "hub_done", saved: false });
|
|
11725
|
+
} finally {
|
|
11726
|
+
this.processing = false;
|
|
11727
|
+
this.hubOrchestrator = null;
|
|
11728
|
+
}
|
|
11729
|
+
}
|
|
11016
11730
|
async handleChat(content, images) {
|
|
11017
11731
|
if (this.processing) {
|
|
11018
11732
|
this.send({ type: "error", message: "Already processing a request. Use abort first." });
|
|
@@ -12715,7 +13429,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
12715
13429
|
case "test": {
|
|
12716
13430
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
12717
13431
|
try {
|
|
12718
|
-
const { executeTests } = await import("./run-tests-
|
|
13432
|
+
const { executeTests } = await import("./run-tests-O46AI32W.js");
|
|
12719
13433
|
const argStr = args.join(" ").trim();
|
|
12720
13434
|
let testArgs = {};
|
|
12721
13435
|
if (argStr) {
|
|
@@ -12732,7 +13446,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
12732
13446
|
// ── /init ───────────────────────────────────────────────────────
|
|
12733
13447
|
case "init": {
|
|
12734
13448
|
const cwd = process.cwd();
|
|
12735
|
-
const targetPath =
|
|
13449
|
+
const targetPath = join15(cwd, "AICLI.md");
|
|
12736
13450
|
const force = args.includes("--force");
|
|
12737
13451
|
if (existsSync21(targetPath) && !force) {
|
|
12738
13452
|
this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
@@ -12777,9 +13491,9 @@ Use /context reload to load it.` });
|
|
|
12777
13491
|
}
|
|
12778
13492
|
lines.push(` ${exists ? "\u2713" : "\u2013"} ${label.padEnd(14)} ${exists ? filePath + extra : "(not found)"}`);
|
|
12779
13493
|
};
|
|
12780
|
-
checkFile("config.json",
|
|
12781
|
-
checkFile("memory.md",
|
|
12782
|
-
checkFile("dev-state.md",
|
|
13494
|
+
checkFile("config.json", join15(configDir, "config.json"));
|
|
13495
|
+
checkFile("memory.md", join15(configDir, MEMORY_FILE_NAME));
|
|
13496
|
+
checkFile("dev-state.md", join15(configDir, "dev-state.md"));
|
|
12783
13497
|
lines.push("");
|
|
12784
13498
|
if (this.mcpManager) {
|
|
12785
13499
|
lines.push("**MCP Servers:**");
|
|
@@ -12886,7 +13600,7 @@ ${this.config.toFormattedJSON()}
|
|
|
12886
13600
|
const layers = ["\u{1F4DA} **Context Layers:**", ""];
|
|
12887
13601
|
const checkLayer = (label, dir) => {
|
|
12888
13602
|
for (const name2 of CONTEXT_FILE_CANDIDATES) {
|
|
12889
|
-
const fullPath =
|
|
13603
|
+
const fullPath = join15(dir, name2);
|
|
12890
13604
|
if (existsSync21(fullPath)) {
|
|
12891
13605
|
try {
|
|
12892
13606
|
const size = statSync8(fullPath).size;
|
|
@@ -12988,7 +13702,7 @@ It will be included in AI context for subsequent messages.` });
|
|
|
12988
13702
|
// ── /commands ───────────────────────────────────────────────────
|
|
12989
13703
|
case "commands": {
|
|
12990
13704
|
const configDir = this.config.getConfigDir();
|
|
12991
|
-
const commandsDir =
|
|
13705
|
+
const commandsDir = join15(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
12992
13706
|
if (!existsSync21(commandsDir)) {
|
|
12993
13707
|
this.send({ type: "info", message: `No custom commands directory.
|
|
12994
13708
|
Create: ${commandsDir}/ with .md files.` });
|
|
@@ -13015,7 +13729,7 @@ Add .md files to create commands.` });
|
|
|
13015
13729
|
// ── /plugins ────────────────────────────────────────────────────
|
|
13016
13730
|
case "plugins": {
|
|
13017
13731
|
const configDir = this.config.getConfigDir();
|
|
13018
|
-
const pluginsDir =
|
|
13732
|
+
const pluginsDir = join15(configDir, PLUGINS_DIR_NAME);
|
|
13019
13733
|
const pluginTools = this.toolRegistry.listPluginTools();
|
|
13020
13734
|
const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
|
|
13021
13735
|
if (pluginTools.length === 0) {
|
|
@@ -13189,7 +13903,7 @@ Add .md files to create commands.` });
|
|
|
13189
13903
|
}
|
|
13190
13904
|
memoryShow() {
|
|
13191
13905
|
const configDir = this.config.getConfigDir();
|
|
13192
|
-
const memPath =
|
|
13906
|
+
const memPath = join15(configDir, MEMORY_FILE_NAME);
|
|
13193
13907
|
let content = "";
|
|
13194
13908
|
try {
|
|
13195
13909
|
if (existsSync21(memPath)) {
|
|
@@ -13207,7 +13921,7 @@ Add .md files to create commands.` });
|
|
|
13207
13921
|
}
|
|
13208
13922
|
memoryAdd(text) {
|
|
13209
13923
|
const configDir = this.config.getConfigDir();
|
|
13210
|
-
const memPath =
|
|
13924
|
+
const memPath = join15(configDir, MEMORY_FILE_NAME);
|
|
13211
13925
|
try {
|
|
13212
13926
|
mkdirSync10(configDir, { recursive: true });
|
|
13213
13927
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
@@ -13221,7 +13935,7 @@ Add .md files to create commands.` });
|
|
|
13221
13935
|
}
|
|
13222
13936
|
memoryClear() {
|
|
13223
13937
|
const configDir = this.config.getConfigDir();
|
|
13224
|
-
const memPath =
|
|
13938
|
+
const memPath = join15(configDir, MEMORY_FILE_NAME);
|
|
13225
13939
|
try {
|
|
13226
13940
|
writeFileSync9(memPath, "", "utf-8");
|
|
13227
13941
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
@@ -13430,7 +14144,7 @@ Add .md files to create commands.` });
|
|
|
13430
14144
|
*/
|
|
13431
14145
|
findContextFile(dir) {
|
|
13432
14146
|
for (const name of CONTEXT_FILE_CANDIDATES) {
|
|
13433
|
-
const fullPath =
|
|
14147
|
+
const fullPath = join15(dir, name);
|
|
13434
14148
|
try {
|
|
13435
14149
|
if (existsSync21(fullPath)) {
|
|
13436
14150
|
const content = readFileSync14(fullPath, "utf-8").trim();
|
|
@@ -13491,11 +14205,11 @@ Add .md files to create commands.` });
|
|
|
13491
14205
|
const sorted = filtered.sort((a, b) => {
|
|
13492
14206
|
let aIsDir = false, bIsDir = false;
|
|
13493
14207
|
try {
|
|
13494
|
-
aIsDir = statSync8(
|
|
14208
|
+
aIsDir = statSync8(join15(d, a)).isDirectory();
|
|
13495
14209
|
} catch {
|
|
13496
14210
|
}
|
|
13497
14211
|
try {
|
|
13498
|
-
bIsDir = statSync8(
|
|
14212
|
+
bIsDir = statSync8(join15(d, b)).isDirectory();
|
|
13499
14213
|
} catch {
|
|
13500
14214
|
}
|
|
13501
14215
|
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
@@ -13503,7 +14217,7 @@ Add .md files to create commands.` });
|
|
|
13503
14217
|
});
|
|
13504
14218
|
for (let i = 0; i < sorted.length && count < maxEntries; i++) {
|
|
13505
14219
|
const name = sorted[i];
|
|
13506
|
-
const fullPath =
|
|
14220
|
+
const fullPath = join15(d, name);
|
|
13507
14221
|
const isLast = i === sorted.length - 1;
|
|
13508
14222
|
let isDir;
|
|
13509
14223
|
try {
|
|
@@ -13522,7 +14236,7 @@ Add .md files to create commands.` });
|
|
|
13522
14236
|
}
|
|
13523
14237
|
scanProject(cwd) {
|
|
13524
14238
|
const info = { type: "unknown", language: "unknown", configFiles: [], directoryStructure: "" };
|
|
13525
|
-
const check = (file) => existsSync21(
|
|
14239
|
+
const check = (file) => existsSync21(join15(cwd, file));
|
|
13526
14240
|
const configCandidates = [
|
|
13527
14241
|
"package.json",
|
|
13528
14242
|
"tsconfig.json",
|
|
@@ -13549,7 +14263,7 @@ Add .md files to create commands.` });
|
|
|
13549
14263
|
info.type = "node";
|
|
13550
14264
|
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
13551
14265
|
try {
|
|
13552
|
-
const pkg = JSON.parse(readFileSync14(
|
|
14266
|
+
const pkg = JSON.parse(readFileSync14(join15(cwd, "package.json"), "utf-8"));
|
|
13553
14267
|
const scripts = pkg.scripts ?? {};
|
|
13554
14268
|
info.buildCommand = scripts.build ? "npm run build" : void 0;
|
|
13555
14269
|
info.testCommand = scripts.test ? "npm test" : void 0;
|
|
@@ -13700,7 +14414,7 @@ async function setupProxy(configProxy) {
|
|
|
13700
14414
|
|
|
13701
14415
|
// src/web/auth.ts
|
|
13702
14416
|
import { existsSync as existsSync22, readFileSync as readFileSync15, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11, readdirSync as readdirSync10, copyFileSync, renameSync as renameSync3, unlinkSync as unlinkSync5 } from "fs";
|
|
13703
|
-
import { join as
|
|
14417
|
+
import { join as join16 } from "path";
|
|
13704
14418
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
13705
14419
|
var USERS_FILE = "users.json";
|
|
13706
14420
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
@@ -13715,7 +14429,7 @@ var AuthManager = class {
|
|
|
13715
14429
|
db;
|
|
13716
14430
|
constructor(baseDir) {
|
|
13717
14431
|
this.baseDir = baseDir;
|
|
13718
|
-
this.usersFile =
|
|
14432
|
+
this.usersFile = join16(baseDir, USERS_FILE);
|
|
13719
14433
|
this.db = this.loadOrCreate();
|
|
13720
14434
|
}
|
|
13721
14435
|
// ── Public API ─────────────────────────────────────────────────
|
|
@@ -13744,9 +14458,9 @@ var AuthManager = class {
|
|
|
13744
14458
|
}
|
|
13745
14459
|
const salt = randomBytes(16).toString("hex");
|
|
13746
14460
|
const passwordHash = this.hashPassword(password, salt);
|
|
13747
|
-
const dataDir =
|
|
13748
|
-
const fullDataDir =
|
|
13749
|
-
mkdirSync11(
|
|
14461
|
+
const dataDir = join16(USERS_DIR, username);
|
|
14462
|
+
const fullDataDir = join16(this.baseDir, dataDir);
|
|
14463
|
+
mkdirSync11(join16(fullDataDir, "history"), { recursive: true });
|
|
13750
14464
|
const user = {
|
|
13751
14465
|
username,
|
|
13752
14466
|
passwordHash,
|
|
@@ -13861,7 +14575,7 @@ var AuthManager = class {
|
|
|
13861
14575
|
getUserDataDir(username) {
|
|
13862
14576
|
const user = this.db.users.find((u) => u.username === username);
|
|
13863
14577
|
if (!user) throw new Error(`User not found: ${username}`);
|
|
13864
|
-
return
|
|
14578
|
+
return join16(this.baseDir, user.dataDir);
|
|
13865
14579
|
}
|
|
13866
14580
|
/** List all usernames */
|
|
13867
14581
|
listUsers() {
|
|
@@ -13897,30 +14611,30 @@ var AuthManager = class {
|
|
|
13897
14611
|
const err = this.register(username, password);
|
|
13898
14612
|
if (err) return err;
|
|
13899
14613
|
const userDir = this.getUserDataDir(username);
|
|
13900
|
-
const globalConfig =
|
|
14614
|
+
const globalConfig = join16(this.baseDir, "config.json");
|
|
13901
14615
|
if (existsSync22(globalConfig)) {
|
|
13902
14616
|
try {
|
|
13903
14617
|
const content = readFileSync15(globalConfig, "utf-8");
|
|
13904
|
-
writeFileSync10(
|
|
14618
|
+
writeFileSync10(join16(userDir, "config.json"), content, "utf-8");
|
|
13905
14619
|
} catch {
|
|
13906
14620
|
}
|
|
13907
14621
|
}
|
|
13908
|
-
const globalMemory =
|
|
14622
|
+
const globalMemory = join16(this.baseDir, "memory.md");
|
|
13909
14623
|
if (existsSync22(globalMemory)) {
|
|
13910
14624
|
try {
|
|
13911
14625
|
const content = readFileSync15(globalMemory, "utf-8");
|
|
13912
|
-
writeFileSync10(
|
|
14626
|
+
writeFileSync10(join16(userDir, "memory.md"), content, "utf-8");
|
|
13913
14627
|
} catch {
|
|
13914
14628
|
}
|
|
13915
14629
|
}
|
|
13916
|
-
const globalHistory =
|
|
14630
|
+
const globalHistory = join16(this.baseDir, "history");
|
|
13917
14631
|
if (existsSync22(globalHistory)) {
|
|
13918
14632
|
try {
|
|
13919
14633
|
const files = readdirSync10(globalHistory).filter((f) => f.endsWith(".json"));
|
|
13920
|
-
const userHistory =
|
|
14634
|
+
const userHistory = join16(userDir, "history");
|
|
13921
14635
|
for (const f of files) {
|
|
13922
14636
|
try {
|
|
13923
|
-
copyFileSync(
|
|
14637
|
+
copyFileSync(join16(globalHistory, f), join16(userHistory, f));
|
|
13924
14638
|
} catch {
|
|
13925
14639
|
}
|
|
13926
14640
|
}
|
|
@@ -14060,7 +14774,7 @@ async function startWebServer(options = {}) {
|
|
|
14060
14774
|
}
|
|
14061
14775
|
}
|
|
14062
14776
|
let skillManager = null;
|
|
14063
|
-
const skillsDir =
|
|
14777
|
+
const skillsDir = join17(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
14064
14778
|
if (existsSync23(skillsDir)) {
|
|
14065
14779
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
14066
14780
|
skillManager.loadSkills();
|
|
@@ -14130,15 +14844,15 @@ async function startWebServer(options = {}) {
|
|
|
14130
14844
|
next();
|
|
14131
14845
|
};
|
|
14132
14846
|
const moduleDir = getModuleDir();
|
|
14133
|
-
let clientDir =
|
|
14847
|
+
let clientDir = join17(moduleDir, "web", "client");
|
|
14134
14848
|
if (!existsSync23(clientDir)) {
|
|
14135
|
-
clientDir =
|
|
14849
|
+
clientDir = join17(moduleDir, "client");
|
|
14136
14850
|
}
|
|
14137
14851
|
if (!existsSync23(clientDir)) {
|
|
14138
|
-
clientDir =
|
|
14852
|
+
clientDir = join17(moduleDir, "..", "..", "src", "web", "client");
|
|
14139
14853
|
}
|
|
14140
14854
|
if (!existsSync23(clientDir)) {
|
|
14141
|
-
clientDir =
|
|
14855
|
+
clientDir = join17(process.cwd(), "src", "web", "client");
|
|
14142
14856
|
}
|
|
14143
14857
|
console.log(` Static files: ${clientDir}`);
|
|
14144
14858
|
app.use(express.static(clientDir));
|
|
@@ -14207,7 +14921,7 @@ async function startWebServer(options = {}) {
|
|
|
14207
14921
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
14208
14922
|
const cwd = process.cwd();
|
|
14209
14923
|
const prefix = req.query.prefix || "";
|
|
14210
|
-
const targetDir =
|
|
14924
|
+
const targetDir = join17(cwd, prefix);
|
|
14211
14925
|
try {
|
|
14212
14926
|
const canonicalTarget = realpathSync(resolve6(targetDir));
|
|
14213
14927
|
const canonicalCwd = realpathSync(resolve6(cwd));
|
|
@@ -14224,7 +14938,7 @@ async function startWebServer(options = {}) {
|
|
|
14224
14938
|
const entries = readdirSync11(targetDir, { withFileTypes: true });
|
|
14225
14939
|
const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
|
|
14226
14940
|
name: e.name,
|
|
14227
|
-
path: relative3(cwd,
|
|
14941
|
+
path: relative3(cwd, join17(targetDir, e.name)).replace(/\\/g, "/"),
|
|
14228
14942
|
isDir: e.isDirectory()
|
|
14229
14943
|
}));
|
|
14230
14944
|
res.json({ files });
|
|
@@ -14260,7 +14974,7 @@ async function startWebServer(options = {}) {
|
|
|
14260
14974
|
try {
|
|
14261
14975
|
const authUser = req._authUser;
|
|
14262
14976
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
14263
|
-
const filePath =
|
|
14977
|
+
const filePath = join17(histDir, `${id}.json`);
|
|
14264
14978
|
if (!existsSync23(filePath)) {
|
|
14265
14979
|
res.status(404).json({ error: "Session not found" });
|
|
14266
14980
|
return;
|
|
@@ -14289,7 +15003,7 @@ async function startWebServer(options = {}) {
|
|
|
14289
15003
|
return;
|
|
14290
15004
|
}
|
|
14291
15005
|
const cwd = process.cwd();
|
|
14292
|
-
const fullPath = resolve6(
|
|
15006
|
+
const fullPath = resolve6(join17(cwd, filePath));
|
|
14293
15007
|
try {
|
|
14294
15008
|
const canonicalFull = realpathSync(fullPath);
|
|
14295
15009
|
const canonicalCwd = realpathSync(resolve6(cwd));
|
|
@@ -14561,14 +15275,14 @@ function resolveProjectMcpPath() {
|
|
|
14561
15275
|
const cwd = process.cwd();
|
|
14562
15276
|
const gitRoot = getGitRoot(cwd);
|
|
14563
15277
|
const projectRoot = gitRoot ?? cwd;
|
|
14564
|
-
const configPath =
|
|
15278
|
+
const configPath = join17(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
14565
15279
|
return existsSync23(configPath) ? configPath : null;
|
|
14566
15280
|
}
|
|
14567
15281
|
function loadProjectMcpConfig() {
|
|
14568
15282
|
const cwd = process.cwd();
|
|
14569
15283
|
const gitRoot = getGitRoot(cwd);
|
|
14570
15284
|
const projectRoot = gitRoot ?? cwd;
|
|
14571
|
-
const configPath =
|
|
15285
|
+
const configPath = join17(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
14572
15286
|
if (!existsSync23(configPath)) return null;
|
|
14573
15287
|
try {
|
|
14574
15288
|
const raw = JSON.parse(readFileSync16(configPath, "utf-8"));
|