jinzd-ai-cli 0.4.155 → 0.4.156
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-DBOCPVH5.js} +2 -2
- package/dist/{chat-index-IF4EINLQ.js → chat-index-2I7ZHRE5.js} +2 -2
- package/dist/{chunk-D6GJTJQH.js → chunk-3DCAQKVZ.js} +1 -1
- package/dist/{chunk-NFRTSL3N.js → chunk-3SQMKA4I.js} +1 -1
- package/dist/{chunk-B3LFGPU2.js → chunk-3WLEDKOW.js} +1 -1
- package/dist/{chunk-JXSWY54M.js → chunk-7OCOFVQP.js} +1 -1
- package/dist/{chunk-JOJRBV2K.js → chunk-AACNCJMD.js} +1 -1
- package/dist/{chunk-CIZQZ7CC.js → chunk-GQ647SF3.js} +2 -2
- 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-UAPNBQLU.js} +1 -1
- package/dist/{chunk-E5ICQT3P.js → chunk-Z2UJDFJK.js} +4 -4
- package/dist/{ci-34ZQH43L.js → ci-AWA6KC3X.js} +3 -3
- package/dist/{constants-DQ5VJOGS.js → constants-QDTWBWWC.js} +1 -1
- package/dist/{doctor-cli-TSCI4ORL.js → doctor-cli-4MTG6SFH.js} +6 -6
- package/dist/electron-server.js +740 -44
- package/dist/hub-QRDXG527.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-CNURD2ST.js} +2 -2
- package/dist/{run-tests-5CJRMOMI.js → run-tests-NPWSCWP5.js} +1 -1
- package/dist/{server-DVIP7NLW.js → server-HFG2SM3Y.js} +6 -6
- package/dist/{server-35OQV62B.js → server-RRUCZMMM.js} +124 -32
- package/dist/{task-orchestrator-AXSS7ROD.js → task-orchestrator-GF6ZMNUK.js} +6 -6
- package/dist/web/client/app.js +138 -0
- package/dist/web/client/index.html +28 -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-7OCOFVQP.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,8 @@ 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;
|
|
10788
11403
|
/** Pending ask_user promises */
|
|
10789
11404
|
pendingAskUser = /* @__PURE__ */ new Map();
|
|
10790
11405
|
/** Pending auto-pause promises */
|
|
@@ -10923,6 +11538,11 @@ var SessionHandler = class _SessionHandler {
|
|
|
10923
11538
|
this.abortController.abort();
|
|
10924
11539
|
}
|
|
10925
11540
|
return;
|
|
11541
|
+
case "hub_start":
|
|
11542
|
+
return this.handleHubStart(msg);
|
|
11543
|
+
case "hub_abort":
|
|
11544
|
+
this.hubOrchestrator?.abort();
|
|
11545
|
+
return;
|
|
10926
11546
|
case "interjection":
|
|
10927
11547
|
this.userInterjection = msg.content;
|
|
10928
11548
|
return;
|
|
@@ -11013,6 +11633,82 @@ var SessionHandler = class _SessionHandler {
|
|
|
11013
11633
|
}
|
|
11014
11634
|
}
|
|
11015
11635
|
// ── Chat handling ────────────────────────────────────────────────
|
|
11636
|
+
/**
|
|
11637
|
+
* P4: run a multi-agent hub discussion in the browser room. Bridges the
|
|
11638
|
+
* UI-agnostic DiscussionOrchestrator (onEvent) to WebSocket hub_event
|
|
11639
|
+
* messages, then persists the result (P3) so it can be replayed.
|
|
11640
|
+
*/
|
|
11641
|
+
async handleHubStart(msg) {
|
|
11642
|
+
if (this.processing || this.hubOrchestrator) {
|
|
11643
|
+
this.send({ type: "error", message: "Already running a request. Abort first." });
|
|
11644
|
+
return;
|
|
11645
|
+
}
|
|
11646
|
+
const topic = (msg.topic ?? "").trim();
|
|
11647
|
+
if (!topic) {
|
|
11648
|
+
this.send({ type: "error", message: "Hub topic is required." });
|
|
11649
|
+
return;
|
|
11650
|
+
}
|
|
11651
|
+
const preset = getPreset(msg.preset ?? "brainstorm");
|
|
11652
|
+
if (!preset) {
|
|
11653
|
+
this.send({ type: "error", message: `Unknown preset "${msg.preset}".` });
|
|
11654
|
+
return;
|
|
11655
|
+
}
|
|
11656
|
+
const defaultProvider = this.currentProvider;
|
|
11657
|
+
const defaultModel = this.currentModel;
|
|
11658
|
+
const resolution = resolveRoleProviders(
|
|
11659
|
+
preset.roles,
|
|
11660
|
+
{
|
|
11661
|
+
has: (id) => this.providers.has(id),
|
|
11662
|
+
defaultModelFor: (id) => this.providers.has(id) ? this.providers.get(id).info.defaultModel : void 0
|
|
11663
|
+
},
|
|
11664
|
+
defaultProvider,
|
|
11665
|
+
defaultModel,
|
|
11666
|
+
this.providers.listAvailable().map((p) => p.info.id),
|
|
11667
|
+
msg.mix
|
|
11668
|
+
);
|
|
11669
|
+
const roles = resolution.roles;
|
|
11670
|
+
for (const w of resolution.warnings) this.send({ type: "info", message: w });
|
|
11671
|
+
const config = {
|
|
11672
|
+
mode: "discuss",
|
|
11673
|
+
roles,
|
|
11674
|
+
defaultProvider,
|
|
11675
|
+
defaultModel,
|
|
11676
|
+
maxRounds: msg.maxRounds && msg.maxRounds > 0 ? Math.min(msg.maxRounds, 20) : 6,
|
|
11677
|
+
voteConverge: msg.vote === true
|
|
11678
|
+
};
|
|
11679
|
+
this.send({
|
|
11680
|
+
type: "hub_started",
|
|
11681
|
+
topic,
|
|
11682
|
+
maxRounds: config.maxRounds,
|
|
11683
|
+
roles: roles.map((r) => ({ id: r.id, name: r.name, color: r.color, provider: r.provider, model: r.model }))
|
|
11684
|
+
});
|
|
11685
|
+
const orchestrator = new DiscussionOrchestrator(config, this.providers);
|
|
11686
|
+
this.hubOrchestrator = orchestrator;
|
|
11687
|
+
orchestrator.onEvent = (event) => this.send({ type: "hub_event", event });
|
|
11688
|
+
this.processing = true;
|
|
11689
|
+
try {
|
|
11690
|
+
const state2 = await orchestrator.run(topic);
|
|
11691
|
+
let sessionId;
|
|
11692
|
+
let saved = false;
|
|
11693
|
+
if (state2.messages.length > 0) {
|
|
11694
|
+
try {
|
|
11695
|
+
const res = await persistDiscussion(state2, this.config, defaultProvider, defaultModel);
|
|
11696
|
+
sessionId = res.id;
|
|
11697
|
+
saved = true;
|
|
11698
|
+
} catch (err) {
|
|
11699
|
+
this.send({ type: "info", message: `Could not save discussion: ${err.message}` });
|
|
11700
|
+
}
|
|
11701
|
+
}
|
|
11702
|
+
this.send({ type: "hub_done", sessionId, saved });
|
|
11703
|
+
if (saved) this.sendSessionList();
|
|
11704
|
+
} catch (err) {
|
|
11705
|
+
this.send({ type: "error", message: `Hub error: ${err.message}` });
|
|
11706
|
+
this.send({ type: "hub_done", saved: false });
|
|
11707
|
+
} finally {
|
|
11708
|
+
this.processing = false;
|
|
11709
|
+
this.hubOrchestrator = null;
|
|
11710
|
+
}
|
|
11711
|
+
}
|
|
11016
11712
|
async handleChat(content, images) {
|
|
11017
11713
|
if (this.processing) {
|
|
11018
11714
|
this.send({ type: "error", message: "Already processing a request. Use abort first." });
|
|
@@ -12715,7 +13411,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
12715
13411
|
case "test": {
|
|
12716
13412
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
12717
13413
|
try {
|
|
12718
|
-
const { executeTests } = await import("./run-tests-
|
|
13414
|
+
const { executeTests } = await import("./run-tests-NPWSCWP5.js");
|
|
12719
13415
|
const argStr = args.join(" ").trim();
|
|
12720
13416
|
let testArgs = {};
|
|
12721
13417
|
if (argStr) {
|
|
@@ -12732,7 +13428,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
12732
13428
|
// ── /init ───────────────────────────────────────────────────────
|
|
12733
13429
|
case "init": {
|
|
12734
13430
|
const cwd = process.cwd();
|
|
12735
|
-
const targetPath =
|
|
13431
|
+
const targetPath = join15(cwd, "AICLI.md");
|
|
12736
13432
|
const force = args.includes("--force");
|
|
12737
13433
|
if (existsSync21(targetPath) && !force) {
|
|
12738
13434
|
this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
@@ -12777,9 +13473,9 @@ Use /context reload to load it.` });
|
|
|
12777
13473
|
}
|
|
12778
13474
|
lines.push(` ${exists ? "\u2713" : "\u2013"} ${label.padEnd(14)} ${exists ? filePath + extra : "(not found)"}`);
|
|
12779
13475
|
};
|
|
12780
|
-
checkFile("config.json",
|
|
12781
|
-
checkFile("memory.md",
|
|
12782
|
-
checkFile("dev-state.md",
|
|
13476
|
+
checkFile("config.json", join15(configDir, "config.json"));
|
|
13477
|
+
checkFile("memory.md", join15(configDir, MEMORY_FILE_NAME));
|
|
13478
|
+
checkFile("dev-state.md", join15(configDir, "dev-state.md"));
|
|
12783
13479
|
lines.push("");
|
|
12784
13480
|
if (this.mcpManager) {
|
|
12785
13481
|
lines.push("**MCP Servers:**");
|
|
@@ -12886,7 +13582,7 @@ ${this.config.toFormattedJSON()}
|
|
|
12886
13582
|
const layers = ["\u{1F4DA} **Context Layers:**", ""];
|
|
12887
13583
|
const checkLayer = (label, dir) => {
|
|
12888
13584
|
for (const name2 of CONTEXT_FILE_CANDIDATES) {
|
|
12889
|
-
const fullPath =
|
|
13585
|
+
const fullPath = join15(dir, name2);
|
|
12890
13586
|
if (existsSync21(fullPath)) {
|
|
12891
13587
|
try {
|
|
12892
13588
|
const size = statSync8(fullPath).size;
|
|
@@ -12988,7 +13684,7 @@ It will be included in AI context for subsequent messages.` });
|
|
|
12988
13684
|
// ── /commands ───────────────────────────────────────────────────
|
|
12989
13685
|
case "commands": {
|
|
12990
13686
|
const configDir = this.config.getConfigDir();
|
|
12991
|
-
const commandsDir =
|
|
13687
|
+
const commandsDir = join15(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
12992
13688
|
if (!existsSync21(commandsDir)) {
|
|
12993
13689
|
this.send({ type: "info", message: `No custom commands directory.
|
|
12994
13690
|
Create: ${commandsDir}/ with .md files.` });
|
|
@@ -13015,7 +13711,7 @@ Add .md files to create commands.` });
|
|
|
13015
13711
|
// ── /plugins ────────────────────────────────────────────────────
|
|
13016
13712
|
case "plugins": {
|
|
13017
13713
|
const configDir = this.config.getConfigDir();
|
|
13018
|
-
const pluginsDir =
|
|
13714
|
+
const pluginsDir = join15(configDir, PLUGINS_DIR_NAME);
|
|
13019
13715
|
const pluginTools = this.toolRegistry.listPluginTools();
|
|
13020
13716
|
const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
|
|
13021
13717
|
if (pluginTools.length === 0) {
|
|
@@ -13189,7 +13885,7 @@ Add .md files to create commands.` });
|
|
|
13189
13885
|
}
|
|
13190
13886
|
memoryShow() {
|
|
13191
13887
|
const configDir = this.config.getConfigDir();
|
|
13192
|
-
const memPath =
|
|
13888
|
+
const memPath = join15(configDir, MEMORY_FILE_NAME);
|
|
13193
13889
|
let content = "";
|
|
13194
13890
|
try {
|
|
13195
13891
|
if (existsSync21(memPath)) {
|
|
@@ -13207,7 +13903,7 @@ Add .md files to create commands.` });
|
|
|
13207
13903
|
}
|
|
13208
13904
|
memoryAdd(text) {
|
|
13209
13905
|
const configDir = this.config.getConfigDir();
|
|
13210
|
-
const memPath =
|
|
13906
|
+
const memPath = join15(configDir, MEMORY_FILE_NAME);
|
|
13211
13907
|
try {
|
|
13212
13908
|
mkdirSync10(configDir, { recursive: true });
|
|
13213
13909
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
@@ -13221,7 +13917,7 @@ Add .md files to create commands.` });
|
|
|
13221
13917
|
}
|
|
13222
13918
|
memoryClear() {
|
|
13223
13919
|
const configDir = this.config.getConfigDir();
|
|
13224
|
-
const memPath =
|
|
13920
|
+
const memPath = join15(configDir, MEMORY_FILE_NAME);
|
|
13225
13921
|
try {
|
|
13226
13922
|
writeFileSync9(memPath, "", "utf-8");
|
|
13227
13923
|
this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
|
|
@@ -13430,7 +14126,7 @@ Add .md files to create commands.` });
|
|
|
13430
14126
|
*/
|
|
13431
14127
|
findContextFile(dir) {
|
|
13432
14128
|
for (const name of CONTEXT_FILE_CANDIDATES) {
|
|
13433
|
-
const fullPath =
|
|
14129
|
+
const fullPath = join15(dir, name);
|
|
13434
14130
|
try {
|
|
13435
14131
|
if (existsSync21(fullPath)) {
|
|
13436
14132
|
const content = readFileSync14(fullPath, "utf-8").trim();
|
|
@@ -13491,11 +14187,11 @@ Add .md files to create commands.` });
|
|
|
13491
14187
|
const sorted = filtered.sort((a, b) => {
|
|
13492
14188
|
let aIsDir = false, bIsDir = false;
|
|
13493
14189
|
try {
|
|
13494
|
-
aIsDir = statSync8(
|
|
14190
|
+
aIsDir = statSync8(join15(d, a)).isDirectory();
|
|
13495
14191
|
} catch {
|
|
13496
14192
|
}
|
|
13497
14193
|
try {
|
|
13498
|
-
bIsDir = statSync8(
|
|
14194
|
+
bIsDir = statSync8(join15(d, b)).isDirectory();
|
|
13499
14195
|
} catch {
|
|
13500
14196
|
}
|
|
13501
14197
|
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
@@ -13503,7 +14199,7 @@ Add .md files to create commands.` });
|
|
|
13503
14199
|
});
|
|
13504
14200
|
for (let i = 0; i < sorted.length && count < maxEntries; i++) {
|
|
13505
14201
|
const name = sorted[i];
|
|
13506
|
-
const fullPath =
|
|
14202
|
+
const fullPath = join15(d, name);
|
|
13507
14203
|
const isLast = i === sorted.length - 1;
|
|
13508
14204
|
let isDir;
|
|
13509
14205
|
try {
|
|
@@ -13522,7 +14218,7 @@ Add .md files to create commands.` });
|
|
|
13522
14218
|
}
|
|
13523
14219
|
scanProject(cwd) {
|
|
13524
14220
|
const info = { type: "unknown", language: "unknown", configFiles: [], directoryStructure: "" };
|
|
13525
|
-
const check = (file) => existsSync21(
|
|
14221
|
+
const check = (file) => existsSync21(join15(cwd, file));
|
|
13526
14222
|
const configCandidates = [
|
|
13527
14223
|
"package.json",
|
|
13528
14224
|
"tsconfig.json",
|
|
@@ -13549,7 +14245,7 @@ Add .md files to create commands.` });
|
|
|
13549
14245
|
info.type = "node";
|
|
13550
14246
|
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
13551
14247
|
try {
|
|
13552
|
-
const pkg = JSON.parse(readFileSync14(
|
|
14248
|
+
const pkg = JSON.parse(readFileSync14(join15(cwd, "package.json"), "utf-8"));
|
|
13553
14249
|
const scripts = pkg.scripts ?? {};
|
|
13554
14250
|
info.buildCommand = scripts.build ? "npm run build" : void 0;
|
|
13555
14251
|
info.testCommand = scripts.test ? "npm test" : void 0;
|
|
@@ -13700,7 +14396,7 @@ async function setupProxy(configProxy) {
|
|
|
13700
14396
|
|
|
13701
14397
|
// src/web/auth.ts
|
|
13702
14398
|
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
|
|
14399
|
+
import { join as join16 } from "path";
|
|
13704
14400
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
13705
14401
|
var USERS_FILE = "users.json";
|
|
13706
14402
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
@@ -13715,7 +14411,7 @@ var AuthManager = class {
|
|
|
13715
14411
|
db;
|
|
13716
14412
|
constructor(baseDir) {
|
|
13717
14413
|
this.baseDir = baseDir;
|
|
13718
|
-
this.usersFile =
|
|
14414
|
+
this.usersFile = join16(baseDir, USERS_FILE);
|
|
13719
14415
|
this.db = this.loadOrCreate();
|
|
13720
14416
|
}
|
|
13721
14417
|
// ── Public API ─────────────────────────────────────────────────
|
|
@@ -13744,9 +14440,9 @@ var AuthManager = class {
|
|
|
13744
14440
|
}
|
|
13745
14441
|
const salt = randomBytes(16).toString("hex");
|
|
13746
14442
|
const passwordHash = this.hashPassword(password, salt);
|
|
13747
|
-
const dataDir =
|
|
13748
|
-
const fullDataDir =
|
|
13749
|
-
mkdirSync11(
|
|
14443
|
+
const dataDir = join16(USERS_DIR, username);
|
|
14444
|
+
const fullDataDir = join16(this.baseDir, dataDir);
|
|
14445
|
+
mkdirSync11(join16(fullDataDir, "history"), { recursive: true });
|
|
13750
14446
|
const user = {
|
|
13751
14447
|
username,
|
|
13752
14448
|
passwordHash,
|
|
@@ -13861,7 +14557,7 @@ var AuthManager = class {
|
|
|
13861
14557
|
getUserDataDir(username) {
|
|
13862
14558
|
const user = this.db.users.find((u) => u.username === username);
|
|
13863
14559
|
if (!user) throw new Error(`User not found: ${username}`);
|
|
13864
|
-
return
|
|
14560
|
+
return join16(this.baseDir, user.dataDir);
|
|
13865
14561
|
}
|
|
13866
14562
|
/** List all usernames */
|
|
13867
14563
|
listUsers() {
|
|
@@ -13897,30 +14593,30 @@ var AuthManager = class {
|
|
|
13897
14593
|
const err = this.register(username, password);
|
|
13898
14594
|
if (err) return err;
|
|
13899
14595
|
const userDir = this.getUserDataDir(username);
|
|
13900
|
-
const globalConfig =
|
|
14596
|
+
const globalConfig = join16(this.baseDir, "config.json");
|
|
13901
14597
|
if (existsSync22(globalConfig)) {
|
|
13902
14598
|
try {
|
|
13903
14599
|
const content = readFileSync15(globalConfig, "utf-8");
|
|
13904
|
-
writeFileSync10(
|
|
14600
|
+
writeFileSync10(join16(userDir, "config.json"), content, "utf-8");
|
|
13905
14601
|
} catch {
|
|
13906
14602
|
}
|
|
13907
14603
|
}
|
|
13908
|
-
const globalMemory =
|
|
14604
|
+
const globalMemory = join16(this.baseDir, "memory.md");
|
|
13909
14605
|
if (existsSync22(globalMemory)) {
|
|
13910
14606
|
try {
|
|
13911
14607
|
const content = readFileSync15(globalMemory, "utf-8");
|
|
13912
|
-
writeFileSync10(
|
|
14608
|
+
writeFileSync10(join16(userDir, "memory.md"), content, "utf-8");
|
|
13913
14609
|
} catch {
|
|
13914
14610
|
}
|
|
13915
14611
|
}
|
|
13916
|
-
const globalHistory =
|
|
14612
|
+
const globalHistory = join16(this.baseDir, "history");
|
|
13917
14613
|
if (existsSync22(globalHistory)) {
|
|
13918
14614
|
try {
|
|
13919
14615
|
const files = readdirSync10(globalHistory).filter((f) => f.endsWith(".json"));
|
|
13920
|
-
const userHistory =
|
|
14616
|
+
const userHistory = join16(userDir, "history");
|
|
13921
14617
|
for (const f of files) {
|
|
13922
14618
|
try {
|
|
13923
|
-
copyFileSync(
|
|
14619
|
+
copyFileSync(join16(globalHistory, f), join16(userHistory, f));
|
|
13924
14620
|
} catch {
|
|
13925
14621
|
}
|
|
13926
14622
|
}
|
|
@@ -14060,7 +14756,7 @@ async function startWebServer(options = {}) {
|
|
|
14060
14756
|
}
|
|
14061
14757
|
}
|
|
14062
14758
|
let skillManager = null;
|
|
14063
|
-
const skillsDir =
|
|
14759
|
+
const skillsDir = join17(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
14064
14760
|
if (existsSync23(skillsDir)) {
|
|
14065
14761
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
14066
14762
|
skillManager.loadSkills();
|
|
@@ -14130,15 +14826,15 @@ async function startWebServer(options = {}) {
|
|
|
14130
14826
|
next();
|
|
14131
14827
|
};
|
|
14132
14828
|
const moduleDir = getModuleDir();
|
|
14133
|
-
let clientDir =
|
|
14829
|
+
let clientDir = join17(moduleDir, "web", "client");
|
|
14134
14830
|
if (!existsSync23(clientDir)) {
|
|
14135
|
-
clientDir =
|
|
14831
|
+
clientDir = join17(moduleDir, "client");
|
|
14136
14832
|
}
|
|
14137
14833
|
if (!existsSync23(clientDir)) {
|
|
14138
|
-
clientDir =
|
|
14834
|
+
clientDir = join17(moduleDir, "..", "..", "src", "web", "client");
|
|
14139
14835
|
}
|
|
14140
14836
|
if (!existsSync23(clientDir)) {
|
|
14141
|
-
clientDir =
|
|
14837
|
+
clientDir = join17(process.cwd(), "src", "web", "client");
|
|
14142
14838
|
}
|
|
14143
14839
|
console.log(` Static files: ${clientDir}`);
|
|
14144
14840
|
app.use(express.static(clientDir));
|
|
@@ -14207,7 +14903,7 @@ async function startWebServer(options = {}) {
|
|
|
14207
14903
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
14208
14904
|
const cwd = process.cwd();
|
|
14209
14905
|
const prefix = req.query.prefix || "";
|
|
14210
|
-
const targetDir =
|
|
14906
|
+
const targetDir = join17(cwd, prefix);
|
|
14211
14907
|
try {
|
|
14212
14908
|
const canonicalTarget = realpathSync(resolve6(targetDir));
|
|
14213
14909
|
const canonicalCwd = realpathSync(resolve6(cwd));
|
|
@@ -14224,7 +14920,7 @@ async function startWebServer(options = {}) {
|
|
|
14224
14920
|
const entries = readdirSync11(targetDir, { withFileTypes: true });
|
|
14225
14921
|
const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
|
|
14226
14922
|
name: e.name,
|
|
14227
|
-
path: relative3(cwd,
|
|
14923
|
+
path: relative3(cwd, join17(targetDir, e.name)).replace(/\\/g, "/"),
|
|
14228
14924
|
isDir: e.isDirectory()
|
|
14229
14925
|
}));
|
|
14230
14926
|
res.json({ files });
|
|
@@ -14260,7 +14956,7 @@ async function startWebServer(options = {}) {
|
|
|
14260
14956
|
try {
|
|
14261
14957
|
const authUser = req._authUser;
|
|
14262
14958
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
14263
|
-
const filePath =
|
|
14959
|
+
const filePath = join17(histDir, `${id}.json`);
|
|
14264
14960
|
if (!existsSync23(filePath)) {
|
|
14265
14961
|
res.status(404).json({ error: "Session not found" });
|
|
14266
14962
|
return;
|
|
@@ -14289,7 +14985,7 @@ async function startWebServer(options = {}) {
|
|
|
14289
14985
|
return;
|
|
14290
14986
|
}
|
|
14291
14987
|
const cwd = process.cwd();
|
|
14292
|
-
const fullPath = resolve6(
|
|
14988
|
+
const fullPath = resolve6(join17(cwd, filePath));
|
|
14293
14989
|
try {
|
|
14294
14990
|
const canonicalFull = realpathSync(fullPath);
|
|
14295
14991
|
const canonicalCwd = realpathSync(resolve6(cwd));
|
|
@@ -14561,14 +15257,14 @@ function resolveProjectMcpPath() {
|
|
|
14561
15257
|
const cwd = process.cwd();
|
|
14562
15258
|
const gitRoot = getGitRoot(cwd);
|
|
14563
15259
|
const projectRoot = gitRoot ?? cwd;
|
|
14564
|
-
const configPath =
|
|
15260
|
+
const configPath = join17(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
14565
15261
|
return existsSync23(configPath) ? configPath : null;
|
|
14566
15262
|
}
|
|
14567
15263
|
function loadProjectMcpConfig() {
|
|
14568
15264
|
const cwd = process.cwd();
|
|
14569
15265
|
const gitRoot = getGitRoot(cwd);
|
|
14570
15266
|
const projectRoot = gitRoot ?? cwd;
|
|
14571
|
-
const configPath =
|
|
15267
|
+
const configPath = join17(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
14572
15268
|
if (!existsSync23(configPath)) return null;
|
|
14573
15269
|
try {
|
|
14574
15270
|
const raw = JSON.parse(readFileSync16(configPath, "utf-8"));
|