gossipcat 0.4.0 → 0.4.2
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/README.md +14 -11
- package/dist-mcp/mcp-server.js +478 -284
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -168,8 +168,7 @@ Both types participate equally in consensus, cross-review, and skill development
|
|
|
168
168
|
### One-liner
|
|
169
169
|
|
|
170
170
|
```bash
|
|
171
|
-
npm install -g
|
|
172
|
-
claude mcp add gossipcat -s user -- gossipcat
|
|
171
|
+
npm install -g gossipcat && claude mcp add gossipcat -s user -- gossipcat
|
|
173
172
|
```
|
|
174
173
|
|
|
175
174
|
Restart Claude Code. Then in any project, ask:
|
|
@@ -185,8 +184,7 @@ Add to `~/.claude/mcp_settings.json`:
|
|
|
185
184
|
{
|
|
186
185
|
"mcpServers": {
|
|
187
186
|
"gossipcat": {
|
|
188
|
-
"command": "gossipcat"
|
|
189
|
-
"args": ["mcp"]
|
|
187
|
+
"command": "gossipcat"
|
|
190
188
|
}
|
|
191
189
|
}
|
|
192
190
|
}
|
|
@@ -199,7 +197,7 @@ Or project-local in `.mcp.json`:
|
|
|
199
197
|
"mcpServers": {
|
|
200
198
|
"gossipcat": {
|
|
201
199
|
"command": "npx",
|
|
202
|
-
"args": ["gossipcat"
|
|
200
|
+
"args": ["gossipcat"]
|
|
203
201
|
}
|
|
204
202
|
}
|
|
205
203
|
}
|
|
@@ -209,7 +207,7 @@ Or project-local in `.mcp.json`:
|
|
|
209
207
|
|
|
210
208
|
Claude Code will call `gossip_setup()` to scaffold `.gossip/config.json` and your agent team. First-run bootstrap also writes the dispatch rules and tool catalog so Claude Code knows how to use gossipcat — no manual config needed.
|
|
211
209
|
|
|
212
|
-
Gossipcat
|
|
210
|
+
Gossipcat is on **[npm](https://www.npmjs.com/package/gossipcat)** and **[GitHub Releases](https://github.com/gossipcat-ai/gossipcat-ai/releases)** — both carry the same bundle. `npm install -g gossipcat` pulls from the registry and is the shortest path; the GitHub release URL is useful when you want to pin to a specific tarball (see [Alternative install paths](#alternative-install-paths) below). Either way, npm drops a `gossipcat` binary on your `PATH`.
|
|
213
211
|
|
|
214
212
|
### What the install ships
|
|
215
213
|
|
|
@@ -222,15 +220,20 @@ Gossipcat ships from **[GitHub Releases](https://github.com/gossipcat-ai/gossipc
|
|
|
222
220
|
|
|
223
221
|
### Alternative install paths
|
|
224
222
|
|
|
225
|
-
**Pin to a specific version:**
|
|
223
|
+
**Pin to a specific npm version:**
|
|
224
|
+
```bash
|
|
225
|
+
npm install -g gossipcat@0.4.1
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Pin to a specific GitHub release tarball** (version-locked, bypasses npm registry):
|
|
226
229
|
```bash
|
|
227
|
-
npm install -g https://github.com/gossipcat-ai/gossipcat-ai/releases/download/v0.
|
|
230
|
+
npm install -g https://github.com/gossipcat-ai/gossipcat-ai/releases/download/v0.4.1/gossipcat-0.4.1.tgz
|
|
228
231
|
```
|
|
229
232
|
|
|
230
233
|
**Project-local install** (each project gets its own gossipcat):
|
|
231
234
|
```bash
|
|
232
235
|
cd your-project
|
|
233
|
-
npm install --save-dev
|
|
236
|
+
npm install --save-dev gossipcat
|
|
234
237
|
```
|
|
235
238
|
The postinstall writes `.mcp.json` to your project root. Open Claude Code in that directory and gossipcat connects automatically — no `claude mcp add` needed.
|
|
236
239
|
|
|
@@ -245,9 +248,9 @@ claude mcp add gossipcat -s user -- node "$PWD/dist-mcp/mcp-server.js"
|
|
|
245
248
|
|
|
246
249
|
### Upgrading
|
|
247
250
|
|
|
248
|
-
Re-run the
|
|
251
|
+
Re-run the install — npm will fetch the latest version and replace the installed binary:
|
|
249
252
|
```bash
|
|
250
|
-
npm install -g
|
|
253
|
+
npm install -g gossipcat@latest
|
|
251
254
|
```
|
|
252
255
|
Or in-session, ask Claude Code: *"Check for gossipcat updates"* — the `gossip_update` tool fetches the latest release notes and applies the upgrade with your confirmation.
|
|
253
256
|
|
package/dist-mcp/mcp-server.js
CHANGED
|
@@ -52,9 +52,13 @@ var init_mcp_context = __esm({
|
|
|
52
52
|
nativeTaskMap: /* @__PURE__ */ new Map(),
|
|
53
53
|
nativeResultMap: /* @__PURE__ */ new Map(),
|
|
54
54
|
nativeAgentConfigs: /* @__PURE__ */ new Map(),
|
|
55
|
+
identityRegistry: /* @__PURE__ */ new Map(),
|
|
55
56
|
pendingConsensusRounds: /* @__PURE__ */ new Map(),
|
|
56
57
|
nativeUtilityConfig: null,
|
|
57
58
|
mainProvider: "google",
|
|
59
|
+
mainModel: "gemini-2.5-pro",
|
|
60
|
+
mainProviderConfig: "google",
|
|
61
|
+
mainModelConfig: "gemini-2.5-pro",
|
|
58
62
|
httpMcpPort: null,
|
|
59
63
|
relayPortSource: null,
|
|
60
64
|
httpMcpPortSource: null,
|
|
@@ -795,10 +799,15 @@ var init_task_dispatcher = __esm({
|
|
|
795
799
|
* Decompose a task into a DispatchPlan using the LLM.
|
|
796
800
|
* On parse failure, falls back to a single sub-task.
|
|
797
801
|
*/
|
|
798
|
-
|
|
802
|
+
/**
|
|
803
|
+
* Build the LLM messages used by decompose(). Exposed so native-utility
|
|
804
|
+
* orchestrators (Claude Code) can dispatch the decomposition as an Agent()
|
|
805
|
+
* call and feed the raw result back through decomposeFromRaw().
|
|
806
|
+
*/
|
|
807
|
+
buildDecomposeMessages(task) {
|
|
799
808
|
const availableSkills = this.getAvailableSkills();
|
|
800
809
|
const skillList = availableSkills.length > 0 ? availableSkills.join(", ") : "general";
|
|
801
|
-
|
|
810
|
+
return [
|
|
802
811
|
{
|
|
803
812
|
role: "system",
|
|
804
813
|
content: `You are a task decomposition engine. Break work into tasks that use the FULL team.
|
|
@@ -834,36 +843,55 @@ Use "sequential" ONLY when a later task genuinely needs output from an earlier o
|
|
|
834
843
|
},
|
|
835
844
|
{ role: "user", content: task }
|
|
836
845
|
];
|
|
837
|
-
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Parse a raw LLM response into a DispatchPlan. Used by both the in-process
|
|
849
|
+
* decompose() path and the native-utility re-entry path — same fallback
|
|
850
|
+
* logic, one place to keep it honest.
|
|
851
|
+
*
|
|
852
|
+
* Strategy/subtask shape validation was previously implicit (trusted-LLM
|
|
853
|
+
* output). The native-utility path feeds raw subagent output through here
|
|
854
|
+
* untrusted, so we validate explicitly: unknown strategies fall back to
|
|
855
|
+
* 'single', non-string descriptions or missing description fields skip the
|
|
856
|
+
* subtask, and if nothing survives we fall through to the single-task
|
|
857
|
+
* default. This is F17 hardening from consensus 0a7c34cb-91624bd4.
|
|
858
|
+
*/
|
|
859
|
+
decomposeFromRaw(task, rawText) {
|
|
860
|
+
const VALID_STRATEGIES = /* @__PURE__ */ new Set(["single", "parallel", "sequential"]);
|
|
861
|
+
const singleTaskFallback = () => ({
|
|
862
|
+
originalTask: task,
|
|
863
|
+
strategy: "single",
|
|
864
|
+
subTasks: [{
|
|
865
|
+
id: (0, import_crypto2.randomUUID)(),
|
|
866
|
+
description: task,
|
|
867
|
+
requiredSkills: [],
|
|
868
|
+
status: "pending"
|
|
869
|
+
}],
|
|
870
|
+
warnings: []
|
|
871
|
+
});
|
|
838
872
|
try {
|
|
839
|
-
const jsonMatch =
|
|
873
|
+
const jsonMatch = rawText.match(/\{[\s\S]*\}/);
|
|
840
874
|
if (!jsonMatch) throw new Error("No JSON in response");
|
|
841
875
|
const plan = JSON.parse(jsonMatch[0]);
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
};
|
|
876
|
+
const strategy = VALID_STRATEGIES.has(plan.strategy) ? plan.strategy : "single";
|
|
877
|
+
const rawSubTasks = Array.isArray(plan.subTasks) ? plan.subTasks : [];
|
|
878
|
+
const subTasks = rawSubTasks.filter((st) => st && typeof st.description === "string" && st.description.trim().length > 0).map((st) => ({
|
|
879
|
+
id: (0, import_crypto2.randomUUID)(),
|
|
880
|
+
description: st.description,
|
|
881
|
+
requiredSkills: Array.isArray(st.requiredSkills) ? st.requiredSkills.filter((s) => typeof s === "string") : [],
|
|
882
|
+
status: "pending"
|
|
883
|
+
}));
|
|
884
|
+
if (subTasks.length === 0) return singleTaskFallback();
|
|
885
|
+
return { originalTask: task, strategy, subTasks, warnings: [] };
|
|
853
886
|
} catch {
|
|
854
|
-
return
|
|
855
|
-
originalTask: task,
|
|
856
|
-
strategy: "single",
|
|
857
|
-
subTasks: [{
|
|
858
|
-
id: (0, import_crypto2.randomUUID)(),
|
|
859
|
-
description: task,
|
|
860
|
-
requiredSkills: [],
|
|
861
|
-
status: "pending"
|
|
862
|
-
}],
|
|
863
|
-
warnings: []
|
|
864
|
-
};
|
|
887
|
+
return singleTaskFallback();
|
|
865
888
|
}
|
|
866
889
|
}
|
|
890
|
+
async decompose(task) {
|
|
891
|
+
const messages = this.buildDecomposeMessages(task);
|
|
892
|
+
const response = await this.llm.generate(messages, { temperature: 0 });
|
|
893
|
+
return this.decomposeFromRaw(task, response.text);
|
|
894
|
+
}
|
|
867
895
|
/**
|
|
868
896
|
* Assign agents to each sub-task by skill match.
|
|
869
897
|
* Modifies the plan in-place and returns it.
|
|
@@ -935,13 +963,22 @@ ${subTaskList}` }
|
|
|
935
963
|
};
|
|
936
964
|
});
|
|
937
965
|
} catch {
|
|
938
|
-
return
|
|
939
|
-
agentId: st.assignedAgent || "",
|
|
940
|
-
task: st.description,
|
|
941
|
-
access: "read"
|
|
942
|
-
}));
|
|
966
|
+
return this.classifyWriteModesFallback(plan);
|
|
943
967
|
}
|
|
944
968
|
}
|
|
969
|
+
/**
|
|
970
|
+
* All-read fallback mapping used when no LLM is available (e.g. pure-native
|
|
971
|
+
* teams using the native-utility path for decomposition but lacking a
|
|
972
|
+
* second round-trip budget for classification). Also used internally by
|
|
973
|
+
* classifyWriteModes() on LLM failure.
|
|
974
|
+
*/
|
|
975
|
+
classifyWriteModesFallback(plan) {
|
|
976
|
+
return plan.subTasks.map((st) => ({
|
|
977
|
+
agentId: st.assignedAgent || "",
|
|
978
|
+
task: st.description,
|
|
979
|
+
access: "read"
|
|
980
|
+
}));
|
|
981
|
+
}
|
|
945
982
|
/** Collect all unique skills from registered agents */
|
|
946
983
|
getAvailableSkills() {
|
|
947
984
|
const skills = /* @__PURE__ */ new Set();
|
|
@@ -9537,57 +9574,66 @@ Before completing:
|
|
|
9537
9574
|
3. Confirm referenced functions/methods exist${fileList}`;
|
|
9538
9575
|
}
|
|
9539
9576
|
function assemblePrompt(parts) {
|
|
9540
|
-
const
|
|
9577
|
+
const prefix = [];
|
|
9578
|
+
const suffix = [];
|
|
9579
|
+
if (parts.identity) {
|
|
9580
|
+
prefix.push(parts.identity);
|
|
9581
|
+
}
|
|
9541
9582
|
if (parts.projectStructure) {
|
|
9542
|
-
|
|
9583
|
+
prefix.push(`
|
|
9543
9584
|
|
|
9544
9585
|
--- PROJECT ---
|
|
9545
9586
|
${parts.projectStructure}
|
|
9546
9587
|
--- END PROJECT ---`);
|
|
9547
9588
|
}
|
|
9548
9589
|
if (parts.chainContext) {
|
|
9549
|
-
|
|
9590
|
+
prefix.push(`
|
|
9550
9591
|
|
|
9551
9592
|
${parts.chainContext}`);
|
|
9593
|
+
}
|
|
9594
|
+
if (parts.instructions) {
|
|
9595
|
+
prefix.push(`
|
|
9596
|
+
|
|
9597
|
+
${parts.instructions}`);
|
|
9552
9598
|
}
|
|
9553
9599
|
if (parts.skills) {
|
|
9554
|
-
|
|
9600
|
+
prefix.push(`
|
|
9555
9601
|
|
|
9556
9602
|
--- SKILLS ---
|
|
9557
9603
|
${parts.skills}
|
|
9558
9604
|
--- END SKILLS ---`);
|
|
9559
9605
|
}
|
|
9560
9606
|
if (parts.consensusSummary) {
|
|
9561
|
-
|
|
9607
|
+
suffix.push({ priority: 0, text: `
|
|
9562
9608
|
|
|
9563
9609
|
--- CONSENSUS OUTPUT FORMAT ---
|
|
9564
9610
|
${CONSENSUS_OUTPUT_FORMAT}
|
|
9565
9611
|
|
|
9566
9612
|
This section will be used for cross-review with peer agents.
|
|
9567
|
-
--- END CONSENSUS OUTPUT FORMAT ---`);
|
|
9613
|
+
--- END CONSENSUS OUTPUT FORMAT ---` });
|
|
9568
9614
|
} else {
|
|
9569
|
-
const hasAnyMeaningfulPart = !!(parts.memory || parts.memoryDir || parts.lens || parts.skills || parts.context || parts.sessionContext || parts.chainContext || parts.specReviewContext || parts.projectStructure || parts.consensusFindings && parts.consensusFindings.length > 0);
|
|
9615
|
+
const hasAnyMeaningfulPart = !!(parts.identity || parts.instructions || parts.memory || parts.memoryDir || parts.lens || parts.skills || parts.context || parts.sessionContext || parts.chainContext || parts.specReviewContext || parts.projectStructure || parts.task || parts.consensusFindings && parts.consensusFindings.length > 0);
|
|
9570
9616
|
if (hasAnyMeaningfulPart) {
|
|
9571
|
-
|
|
9617
|
+
suffix.push({ priority: 0, text: `
|
|
9572
9618
|
|
|
9573
9619
|
--- FINDING TAG SCHEMA ---
|
|
9574
9620
|
${FINDING_TAG_SCHEMA}
|
|
9575
|
-
--- END FINDING TAG SCHEMA ---`);
|
|
9621
|
+
--- END FINDING TAG SCHEMA ---` });
|
|
9576
9622
|
}
|
|
9577
9623
|
}
|
|
9578
9624
|
if (parts.lens) {
|
|
9579
|
-
|
|
9625
|
+
suffix.push({ priority: 1, text: `
|
|
9580
9626
|
|
|
9581
9627
|
--- LENS ---
|
|
9582
9628
|
${parts.lens}
|
|
9583
|
-
--- END LENS ---`);
|
|
9629
|
+
--- END LENS ---` });
|
|
9584
9630
|
}
|
|
9585
9631
|
if (parts.specReviewContext) {
|
|
9586
|
-
|
|
9632
|
+
suffix.push({ priority: 2, text: `
|
|
9587
9633
|
|
|
9588
9634
|
--- SPEC REVIEW ---
|
|
9589
9635
|
${parts.specReviewContext}
|
|
9590
|
-
--- END SPEC REVIEW ---`);
|
|
9636
|
+
--- END SPEC REVIEW ---` });
|
|
9591
9637
|
}
|
|
9592
9638
|
if (parts.memory || parts.consensusFindings && parts.consensusFindings.length > 0) {
|
|
9593
9639
|
const memParts = [];
|
|
@@ -9596,14 +9642,14 @@ ${parts.specReviewContext}
|
|
|
9596
9642
|
const findingsBlock = "### Recent Consensus Findings\n" + parts.consensusFindings.map((f, i) => `${i + 1}. ${f}`).join("\n");
|
|
9597
9643
|
memParts.push(findingsBlock);
|
|
9598
9644
|
}
|
|
9599
|
-
|
|
9645
|
+
suffix.push({ priority: 3, text: `
|
|
9600
9646
|
|
|
9601
9647
|
--- MEMORY ---
|
|
9602
9648
|
${memParts.join("\n\n")}
|
|
9603
|
-
--- END MEMORY ---`);
|
|
9649
|
+
--- END MEMORY ---` });
|
|
9604
9650
|
}
|
|
9605
9651
|
if (parts.memoryDir) {
|
|
9606
|
-
|
|
9652
|
+
suffix.push({ priority: 4, text: `
|
|
9607
9653
|
|
|
9608
9654
|
--- AGENT MEMORY ---
|
|
9609
9655
|
Your persistent memory directory: ${parts.memoryDir}
|
|
@@ -9611,27 +9657,69 @@ Save important learnings using file_write to this directory.
|
|
|
9611
9657
|
What to save: technology choices, file structure, key patterns, architectural decisions, gotchas.
|
|
9612
9658
|
Use descriptive filenames like: tech-stack.md, project-structure.md, patterns.md
|
|
9613
9659
|
Keep entries concise (5-10 lines each). Update existing files rather than creating new ones.
|
|
9614
|
-
--- END AGENT MEMORY ---`);
|
|
9660
|
+
--- END AGENT MEMORY ---` });
|
|
9615
9661
|
}
|
|
9616
9662
|
if (parts.sessionContext) {
|
|
9617
|
-
|
|
9663
|
+
suffix.push({ priority: 5, text: `
|
|
9618
9664
|
|
|
9619
|
-
${parts.sessionContext}`);
|
|
9665
|
+
${parts.sessionContext}` });
|
|
9620
9666
|
}
|
|
9621
9667
|
if (parts.context) {
|
|
9622
|
-
|
|
9668
|
+
suffix.push({ priority: 6, text: `
|
|
9623
9669
|
|
|
9624
9670
|
Context:
|
|
9625
|
-
${parts.context}`);
|
|
9671
|
+
${parts.context}` });
|
|
9626
9672
|
}
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9673
|
+
if (parts.task) {
|
|
9674
|
+
suffix.push({ priority: 0, text: `
|
|
9675
|
+
|
|
9676
|
+
---
|
|
9677
|
+
|
|
9678
|
+
Task: ${parts.task}` });
|
|
9679
|
+
}
|
|
9680
|
+
const SUFFIX_RESERVE = Math.floor(MAX_ASSEMBLED_PROMPT_CHARS * 0.6);
|
|
9681
|
+
const dropOrderDesc = [...suffix].sort((a, b) => b.priority - a.priority);
|
|
9682
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
9683
|
+
const suffixLen = () => suffix.filter((_, i) => !dropped.has(i)).reduce((acc, seg) => acc + seg.text.length, 0);
|
|
9684
|
+
for (const seg of dropOrderDesc) {
|
|
9685
|
+
if (seg.priority === 0) break;
|
|
9686
|
+
if (suffixLen() <= SUFFIX_RESERVE) break;
|
|
9687
|
+
const idx = suffix.indexOf(seg);
|
|
9688
|
+
if (idx >= 0) dropped.add(idx);
|
|
9689
|
+
}
|
|
9690
|
+
const suffixStr = suffix.filter((_, i) => !dropped.has(i)).map((seg) => seg.text).join("");
|
|
9691
|
+
let prefixStr = prefix.join("");
|
|
9692
|
+
const budget = Math.max(0, MAX_ASSEMBLED_PROMPT_CHARS - suffixStr.length);
|
|
9693
|
+
if (prefixStr.length > budget) {
|
|
9694
|
+
prefixStr = prefixStr.slice(0, budget) + "\n\n[Context truncated to fit budget]";
|
|
9695
|
+
}
|
|
9696
|
+
return prefixStr + suffixStr;
|
|
9697
|
+
}
|
|
9698
|
+
function assembleUtilityPrompt(args) {
|
|
9699
|
+
const { taskId, modelShort, system, user, intro, reentrantCall, relayToken } = args;
|
|
9700
|
+
const relayArgs = relayToken ? `task_id: "${taskId}", relay_token: "${relayToken}", result: "<full agent output>"` : `task_id: "${taskId}", result: "<full agent output>"`;
|
|
9701
|
+
return [
|
|
9702
|
+
{
|
|
9703
|
+
type: "text",
|
|
9704
|
+
text: `${intro}
|
|
9705
|
+
|
|
9706
|
+
\u26A0\uFE0F EXECUTE NOW \u2014 launch this Agent and re-call the tool:
|
|
9707
|
+
|
|
9708
|
+
1. Agent(model: "${modelShort}", prompt: <AGENT_PROMPT:${taskId} below>, run_in_background: true) \u2014 pass the AGENT_PROMPT:${taskId} content item verbatim
|
|
9709
|
+
2. When agent completes \u2192 gossip_relay(${relayArgs})
|
|
9710
|
+
3. Then re-call: ${reentrantCall}
|
|
9711
|
+
|
|
9712
|
+
Do ALL steps in order. Do not wait for user input between them.`
|
|
9713
|
+
},
|
|
9714
|
+
{ type: "text", text: `AGENT_PROMPT:${taskId} (_utility)
|
|
9715
|
+
${system}
|
|
9716
|
+
|
|
9717
|
+
---
|
|
9718
|
+
|
|
9719
|
+
${user}` }
|
|
9720
|
+
];
|
|
9633
9721
|
}
|
|
9634
|
-
var path, DOC_EXTENSIONS, SPEC_PATH_PATTERN, FILE_REF_PATTERN;
|
|
9722
|
+
var path, DOC_EXTENSIONS, SPEC_PATH_PATTERN, FILE_REF_PATTERN, MAX_ASSEMBLED_PROMPT_CHARS;
|
|
9635
9723
|
var init_prompt_assembler = __esm({
|
|
9636
9724
|
"packages/orchestrator/src/prompt-assembler.ts"() {
|
|
9637
9725
|
"use strict";
|
|
@@ -9640,6 +9728,7 @@ var init_prompt_assembler = __esm({
|
|
|
9640
9728
|
DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".txt", ".rst"]);
|
|
9641
9729
|
SPEC_PATH_PATTERN = /(?:docs\/|specs\/|[\w-]+-(?:design|spec)\.md)/;
|
|
9642
9730
|
FILE_REF_PATTERN = /(?:`([^`]+\.[a-z]{1,6})`|([a-zA-Z][\w/.@-]+\.[a-z]{1,6})(?::\d+)?)/g;
|
|
9731
|
+
MAX_ASSEMBLED_PROMPT_CHARS = 3e4;
|
|
9643
9732
|
}
|
|
9644
9733
|
});
|
|
9645
9734
|
|
|
@@ -12787,6 +12876,20 @@ Return ONLY a JSON array. Use findingId to reference findings:
|
|
|
12787
12876
|
[
|
|
12788
12877
|
{ "action": "agree"|"disagree"|"unverified"|"new", "findingId": "agent:f1", "finding": "brief summary", "evidence": "your reasoning", "confidence": 1-5 }
|
|
12789
12878
|
]`;
|
|
12879
|
+
let skillsBlock = "";
|
|
12880
|
+
if (this.config.getAgentSkillsContent) {
|
|
12881
|
+
try {
|
|
12882
|
+
const skills = this.config.getAgentSkillsContent(agent.agentId, agent.task);
|
|
12883
|
+
if (skills && skills.trim().length > 0) {
|
|
12884
|
+
skillsBlock = `
|
|
12885
|
+
|
|
12886
|
+
--- SKILLS ---
|
|
12887
|
+
${skills}
|
|
12888
|
+
--- END SKILLS ---`;
|
|
12889
|
+
}
|
|
12890
|
+
} catch {
|
|
12891
|
+
}
|
|
12892
|
+
}
|
|
12790
12893
|
const system = `You are a code reviewer performing cross-review. Your job is to verify peer findings against actual code \u2014 catch errors, but also confirm good work.
|
|
12791
12894
|
|
|
12792
12895
|
SOURCE FILES: Always cite original source files, not compiled/bundled build output (dist/, build/, out/). Build artifacts have different line numbers \u2014 citing them causes false verification failures.
|
|
@@ -12801,7 +12904,7 @@ VERIFICATION RULES:
|
|
|
12801
12904
|
- Do NOT agree with a finding just because it sounds plausible \u2014 verify it
|
|
12802
12905
|
- Agreeing without verification is WORSE than disagreeing \u2014 a false confirmation poisons the system
|
|
12803
12906
|
|
|
12804
|
-
Return only valid JSON
|
|
12907
|
+
Return only valid JSON.${skillsBlock}`;
|
|
12805
12908
|
return { system, user };
|
|
12806
12909
|
}
|
|
12807
12910
|
/**
|
|
@@ -14244,6 +14347,7 @@ var init_consensus_coordinator = __esm({
|
|
|
14244
14347
|
registryGet;
|
|
14245
14348
|
projectRoot;
|
|
14246
14349
|
keyProvider;
|
|
14350
|
+
getAgentSkillsContent;
|
|
14247
14351
|
gossipPublisher = null;
|
|
14248
14352
|
memWriter;
|
|
14249
14353
|
currentPhase = "idle";
|
|
@@ -14253,6 +14357,7 @@ var init_consensus_coordinator = __esm({
|
|
|
14253
14357
|
this.registryGet = config2.registryGet;
|
|
14254
14358
|
this.projectRoot = config2.projectRoot;
|
|
14255
14359
|
this.keyProvider = config2.keyProvider;
|
|
14360
|
+
this.getAgentSkillsContent = config2.getAgentSkillsContent;
|
|
14256
14361
|
this.memWriter = new MemoryWriter(config2.projectRoot);
|
|
14257
14362
|
}
|
|
14258
14363
|
setGossipPublisher(publisher) {
|
|
@@ -14288,7 +14393,13 @@ var init_consensus_coordinator = __esm({
|
|
|
14288
14393
|
}
|
|
14289
14394
|
agentLlm = (agentId) => agentLlmCache.get(agentId) ?? void 0;
|
|
14290
14395
|
}
|
|
14291
|
-
const engine = new ConsensusEngine({
|
|
14396
|
+
const engine = new ConsensusEngine({
|
|
14397
|
+
llm: this.llm,
|
|
14398
|
+
registryGet: this.registryGet,
|
|
14399
|
+
projectRoot: this.projectRoot,
|
|
14400
|
+
agentLlm,
|
|
14401
|
+
getAgentSkillsContent: this.getAgentSkillsContent
|
|
14402
|
+
});
|
|
14292
14403
|
const consensusReport = await engine.run(results);
|
|
14293
14404
|
const perfWriter = new PerformanceWriter(this.projectRoot);
|
|
14294
14405
|
this.currentPhase = "cross_review";
|
|
@@ -14587,6 +14698,16 @@ var init_dispatch_pipeline = __esm({
|
|
|
14587
14698
|
this.projectStructureCache = parts.length > 0 ? parts.join("\n") : "";
|
|
14588
14699
|
return this.projectStructureCache || void 0;
|
|
14589
14700
|
}
|
|
14701
|
+
/**
|
|
14702
|
+
* Invalidate the cached project structure so the next prompt regenerates it.
|
|
14703
|
+
* Call this when the project layout has changed mid-session (e.g. new
|
|
14704
|
+
* top-level packages, agent scaffold, gossip_setup adding agent dirs).
|
|
14705
|
+
* Without this, every prompt sees the boot-time-cached layout and agents
|
|
14706
|
+
* reason against stale structure. Drift audit haiku #8.
|
|
14707
|
+
*/
|
|
14708
|
+
invalidateProjectStructureCache() {
|
|
14709
|
+
this.projectStructureCache = null;
|
|
14710
|
+
}
|
|
14590
14711
|
constructor(config2) {
|
|
14591
14712
|
this.projectRoot = config2.projectRoot;
|
|
14592
14713
|
this.workers = config2.workers;
|
|
@@ -14607,7 +14728,16 @@ var init_dispatch_pipeline = __esm({
|
|
|
14607
14728
|
llm: config2.llm ?? null,
|
|
14608
14729
|
registryGet: config2.registryGet,
|
|
14609
14730
|
projectRoot: config2.projectRoot,
|
|
14610
|
-
keyProvider: config2.keyProvider ?? null
|
|
14731
|
+
keyProvider: config2.keyProvider ?? null,
|
|
14732
|
+
getAgentSkillsContent: (agentId, task) => {
|
|
14733
|
+
const agentSkills = this.registryGet(agentId)?.skills || [];
|
|
14734
|
+
try {
|
|
14735
|
+
const res = loadSkills(agentId, agentSkills, this.projectRoot, this.skillIndex ?? void 0, task);
|
|
14736
|
+
return res.content || void 0;
|
|
14737
|
+
} catch {
|
|
14738
|
+
return void 0;
|
|
14739
|
+
}
|
|
14740
|
+
}
|
|
14611
14741
|
});
|
|
14612
14742
|
try {
|
|
14613
14743
|
this.catalog = new SkillCatalog(config2.projectRoot);
|
|
@@ -17290,6 +17420,9 @@ message: Your question?
|
|
|
17290
17420
|
setSkillIndex(index) {
|
|
17291
17421
|
this.pipeline.setSkillIndex(index);
|
|
17292
17422
|
}
|
|
17423
|
+
invalidateProjectStructureCache() {
|
|
17424
|
+
this.pipeline.invalidateProjectStructureCache();
|
|
17425
|
+
}
|
|
17293
17426
|
setSummaryLlm(llm) {
|
|
17294
17427
|
this.pipeline.setSummaryLlm(llm);
|
|
17295
17428
|
}
|
|
@@ -20554,6 +20687,7 @@ __export(src_exports3, {
|
|
|
20554
20687
|
GeminiProvider: () => GeminiProvider,
|
|
20555
20688
|
GossipPublisher: () => GossipPublisher,
|
|
20556
20689
|
LensGenerator: () => LensGenerator,
|
|
20690
|
+
MAX_ASSEMBLED_PROMPT_CHARS: () => MAX_ASSEMBLED_PROMPT_CHARS,
|
|
20557
20691
|
MIN_AGENTS_FOR_CONSENSUS: () => MIN_AGENTS_FOR_CONSENSUS,
|
|
20558
20692
|
MIN_EVIDENCE: () => MIN_EVIDENCE,
|
|
20559
20693
|
MainAgent: () => MainAgent,
|
|
@@ -20590,10 +20724,12 @@ __export(src_exports3, {
|
|
|
20590
20724
|
WorktreeManager: () => WorktreeManager,
|
|
20591
20725
|
Z_CRITICAL: () => Z_CRITICAL,
|
|
20592
20726
|
assemblePrompt: () => assemblePrompt,
|
|
20727
|
+
assembleUtilityPrompt: () => assembleUtilityPrompt,
|
|
20593
20728
|
buildSpecReviewEnrichment: () => buildSpecReviewEnrichment,
|
|
20594
20729
|
buildToolSystemPrompt: () => buildToolSystemPrompt,
|
|
20595
20730
|
createHttpBridgeServer: () => createHttpBridgeServer,
|
|
20596
20731
|
createProvider: () => createProvider,
|
|
20732
|
+
detectFormatCompliance: () => detectFormatCompliance,
|
|
20597
20733
|
ensureRulesFile: () => ensureRulesFile,
|
|
20598
20734
|
extractCategories: () => extractCategories,
|
|
20599
20735
|
extractSpecReferences: () => extractSpecReferences,
|
|
@@ -20614,6 +20750,7 @@ var init_src4 = __esm({
|
|
|
20614
20750
|
"packages/orchestrator/src/index.ts"() {
|
|
20615
20751
|
"use strict";
|
|
20616
20752
|
init_main_agent();
|
|
20753
|
+
init_dispatch_pipeline();
|
|
20617
20754
|
init_skill_loader();
|
|
20618
20755
|
init_skill_counters();
|
|
20619
20756
|
init_worker_agent();
|
|
@@ -23500,6 +23637,16 @@ var init_server = __esm({
|
|
|
23500
23637
|
connectedAgentIds: this.connectionManager.getAll().map((c) => c.agentId)
|
|
23501
23638
|
});
|
|
23502
23639
|
}
|
|
23640
|
+
/**
|
|
23641
|
+
* Update the dashboard's cached agent configs. Call from the MCP server
|
|
23642
|
+
* after gossip_setup writes config.json so the Team page reflects the new
|
|
23643
|
+
* team without requiring /mcp reconnect. The boot-time snapshot
|
|
23644
|
+
* (mcp-server-sdk.ts:365) still wins on initial boot — this method is a
|
|
23645
|
+
* post-setup override, not a replacement.
|
|
23646
|
+
*/
|
|
23647
|
+
setAgentConfigs(configs) {
|
|
23648
|
+
this.dashboardRouter?.updateContext({ agentConfigs: configs });
|
|
23649
|
+
}
|
|
23503
23650
|
};
|
|
23504
23651
|
}
|
|
23505
23652
|
});
|
|
@@ -38303,6 +38450,36 @@ async function handleNativeRelay(task_id, result, error48, agentStartedAt, relay
|
|
|
38303
38450
|
} catch {
|
|
38304
38451
|
}
|
|
38305
38452
|
}
|
|
38453
|
+
if (!error48 && !taskInfo.utilityType && agentId !== "_utility") {
|
|
38454
|
+
try {
|
|
38455
|
+
const { PerformanceWriter: PerformanceWriter2, detectFormatCompliance: detectFormatCompliance2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
|
|
38456
|
+
const compliance = detectFormatCompliance2(result ?? "");
|
|
38457
|
+
const metaWriter = new PerformanceWriter2(process.cwd());
|
|
38458
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
38459
|
+
metaWriter.appendSignals([
|
|
38460
|
+
{ type: "meta", signal: "task_completed", agentId, taskId: task_id, value: elapsed, timestamp: now },
|
|
38461
|
+
{
|
|
38462
|
+
type: "meta",
|
|
38463
|
+
signal: "format_compliance",
|
|
38464
|
+
agentId,
|
|
38465
|
+
taskId: task_id,
|
|
38466
|
+
value: compliance.formatCompliant ? 1 : 0,
|
|
38467
|
+
metadata: {
|
|
38468
|
+
findingCount: compliance.findingCount,
|
|
38469
|
+
citationCount: compliance.citationCount,
|
|
38470
|
+
tags_total: compliance.tags_total,
|
|
38471
|
+
tags_accepted: compliance.tags_accepted,
|
|
38472
|
+
tags_dropped_unknown_type: compliance.tags_dropped_unknown_type,
|
|
38473
|
+
tags_dropped_short_content: compliance.tags_dropped_short_content
|
|
38474
|
+
},
|
|
38475
|
+
timestamp: now
|
|
38476
|
+
}
|
|
38477
|
+
]);
|
|
38478
|
+
} catch (err) {
|
|
38479
|
+
process.stderr.write(`[gossipcat] native meta signals: ${err.message}
|
|
38480
|
+
`);
|
|
38481
|
+
}
|
|
38482
|
+
}
|
|
38306
38483
|
if (taskInfo.planId && taskInfo.step && !error48) {
|
|
38307
38484
|
try {
|
|
38308
38485
|
ctx.mainAgent.recordPlanStepResult(taskInfo.planId, taskInfo.step, result);
|
|
@@ -38666,23 +38843,13 @@ async function handleDispatchSingle(agent_id, task, write_mode, scope, timeout_m
|
|
|
38666
38843
|
singleSkillIndex,
|
|
38667
38844
|
task
|
|
38668
38845
|
);
|
|
38669
|
-
|
|
38670
|
-
|
|
38671
|
-
|
|
38672
|
-
|
|
38673
|
-
|
|
38674
|
-
|
|
38675
|
-
|
|
38676
|
-
` : "",
|
|
38677
|
-
`
|
|
38678
|
-
---
|
|
38679
|
-
|
|
38680
|
-
Task: ${task}`
|
|
38681
|
-
].filter(Boolean).join("").trim();
|
|
38682
|
-
const MAX_AGENT_PROMPT_CHARS = 3e4;
|
|
38683
|
-
if (agentPrompt.length > MAX_AGENT_PROMPT_CHARS) {
|
|
38684
|
-
agentPrompt = agentPrompt.slice(0, MAX_AGENT_PROMPT_CHARS) + "\n\n[Context truncated to fit budget]";
|
|
38685
|
-
}
|
|
38846
|
+
let agentPrompt = assemblePrompt({
|
|
38847
|
+
identity: buildNativeIdentity(agent_id, nativeConfig.model),
|
|
38848
|
+
instructions: nativeConfig.instructions || void 0,
|
|
38849
|
+
skills: skillResult.content || void 0,
|
|
38850
|
+
chainContext: chainContext || void 0,
|
|
38851
|
+
task
|
|
38852
|
+
});
|
|
38686
38853
|
if (sanitizeResult.sanitized) agentPrompt = prependScopeNote(agentPrompt);
|
|
38687
38854
|
recordDispatchMetadata(process.cwd(), {
|
|
38688
38855
|
taskId,
|
|
@@ -38834,20 +39001,13 @@ async function handleDispatchParallel(taskDefs, consensus) {
|
|
|
38834
39001
|
parallelSkillIndex,
|
|
38835
39002
|
def.task
|
|
38836
39003
|
);
|
|
38837
|
-
|
|
38838
|
-
|
|
38839
|
-
|
|
38840
|
-
|
|
38841
|
-
|
|
38842
|
-
|
|
38843
|
-
|
|
38844
|
-
|
|
38845
|
-
Task: ${def.task}`
|
|
38846
|
-
].filter(Boolean).join("").trim();
|
|
38847
|
-
const MAX_AGENT_PROMPT_CHARS = 3e4;
|
|
38848
|
-
if (agentPrompt.length > MAX_AGENT_PROMPT_CHARS) {
|
|
38849
|
-
agentPrompt = agentPrompt.slice(0, MAX_AGENT_PROMPT_CHARS) + "\n\n[Context truncated to fit budget]";
|
|
38850
|
-
}
|
|
39004
|
+
let agentPrompt = assemblePrompt({
|
|
39005
|
+
identity: buildNativeIdentity(def.agent_id, nativeConfig.model),
|
|
39006
|
+
instructions: nativeConfig.instructions || void 0,
|
|
39007
|
+
skills: skillResult.content || void 0,
|
|
39008
|
+
consensusSummary: consensus || void 0,
|
|
39009
|
+
task: def.task
|
|
39010
|
+
});
|
|
38851
39011
|
if (def._sandboxSanitized) agentPrompt = prependScopeNote(agentPrompt);
|
|
38852
39012
|
recordDispatchMetadata(process.cwd(), {
|
|
38853
39013
|
taskId,
|
|
@@ -38965,11 +39125,6 @@ async function handleDispatchConsensus(taskDefs, _utility_task_id) {
|
|
|
38965
39125
|
}
|
|
38966
39126
|
if (errors.length) lines.push(`Relay errors: ${errors.join(", ")}`);
|
|
38967
39127
|
}
|
|
38968
|
-
const consensusInstruction = `
|
|
38969
|
-
|
|
38970
|
-
--- CONSENSUS OUTPUT FORMAT ---
|
|
38971
|
-
${CONSENSUS_OUTPUT_FORMAT}
|
|
38972
|
-
--- END CONSENSUS OUTPUT FORMAT ---`;
|
|
38973
39128
|
const nativeInstructions = [];
|
|
38974
39129
|
const nativePrompts = [];
|
|
38975
39130
|
for (const def of nativeTasks) {
|
|
@@ -38987,11 +39142,7 @@ ${CONSENSUS_OUTPUT_FORMAT}
|
|
|
38987
39142
|
process.stderr.write(`[gossipcat] dispatch \u2192 ${def.agent_id} (${nativeConfig.model}) [${taskId}]
|
|
38988
39143
|
`);
|
|
38989
39144
|
const rawLens = precomputedLenses?.get(def.agent_id);
|
|
38990
|
-
const
|
|
38991
|
-
|
|
38992
|
-
--- LENS ---
|
|
38993
|
-
${rawLens.replace(/---\s*(END )?LENS\s*---/gi, "")}
|
|
38994
|
-
--- END LENS ---` : "";
|
|
39145
|
+
const lensContent = rawLens ? rawLens.replace(/---\s*(END )?LENS\s*---/gi, "").trim() : void 0;
|
|
38995
39146
|
let consensusSkillIndex;
|
|
38996
39147
|
try {
|
|
38997
39148
|
consensusSkillIndex = ctx.mainAgent.getSkillIndex() ?? void 0;
|
|
@@ -39004,23 +39155,14 @@ ${rawLens.replace(/---\s*(END )?LENS\s*---/gi, "")}
|
|
|
39004
39155
|
consensusSkillIndex,
|
|
39005
39156
|
def.task
|
|
39006
39157
|
);
|
|
39007
|
-
|
|
39008
|
-
|
|
39009
|
-
|
|
39010
|
-
|
|
39011
|
-
|
|
39012
|
-
|
|
39013
|
-
|
|
39014
|
-
|
|
39015
|
-
let prefix = [
|
|
39016
|
-
consensusIdentityBlock,
|
|
39017
|
-
nativeConfig.instructions || "",
|
|
39018
|
-
skillResultC.content
|
|
39019
|
-
].filter(Boolean).join("").trim();
|
|
39020
|
-
if (prefix.length > prefixBudget) {
|
|
39021
|
-
prefix = prefix.slice(0, prefixBudget) + "\n\n[Context truncated to fit budget]";
|
|
39022
|
-
}
|
|
39023
|
-
let agentPrompt = prefix + suffix;
|
|
39158
|
+
let agentPrompt = assemblePrompt({
|
|
39159
|
+
identity: buildNativeIdentity(def.agent_id, nativeConfig.model),
|
|
39160
|
+
instructions: nativeConfig.instructions || void 0,
|
|
39161
|
+
skills: skillResultC.content || void 0,
|
|
39162
|
+
consensusSummary: true,
|
|
39163
|
+
lens: lensContent || void 0,
|
|
39164
|
+
task: def.task
|
|
39165
|
+
});
|
|
39024
39166
|
lines.push(` ${taskId} \u2192 ${def.agent_id} (native \u2014 dispatch via Agent tool)`);
|
|
39025
39167
|
nativeInstructions.push(
|
|
39026
39168
|
`[${taskId}] Agent(model: "${nativeConfig.model}", prompt: <AGENT_PROMPT:${taskId} below>, run_in_background: true)
|
|
@@ -39073,6 +39215,15 @@ init_mcp_context();
|
|
|
39073
39215
|
init_relay_cross_review();
|
|
39074
39216
|
init_src3();
|
|
39075
39217
|
init_src4();
|
|
39218
|
+
var CONSENSUS_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{8}$/;
|
|
39219
|
+
function isValidConsensusId(id) {
|
|
39220
|
+
return typeof id === "string" && CONSENSUS_ID_RE.test(id);
|
|
39221
|
+
}
|
|
39222
|
+
function extractConsensusIdFromFindingId(findingId) {
|
|
39223
|
+
if (typeof findingId !== "string") return void 0;
|
|
39224
|
+
const first = findingId.split(":")[0];
|
|
39225
|
+
return isValidConsensusId(first) ? first : void 0;
|
|
39226
|
+
}
|
|
39076
39227
|
async function handleCollect(task_ids, timeout_ms, consensus) {
|
|
39077
39228
|
await ctx.boot();
|
|
39078
39229
|
if (consensus && (!task_ids || task_ids.length === 0)) {
|
|
@@ -39579,10 +39730,11 @@ ${np.user}
|
|
|
39579
39730
|
...consensusReport.unverified || [],
|
|
39580
39731
|
...consensusReport.unique || []
|
|
39581
39732
|
];
|
|
39582
|
-
const
|
|
39733
|
+
const authoritativeId = consensusReport?.signals?.[0]?.consensusId;
|
|
39734
|
+
const provisionalConsensusId = (isValidConsensusId(authoritativeId) ? authoritativeId : void 0) ?? (allFindings.length > 0 ? extractConsensusIdFromFindingId(allFindings[0].id) : void 0);
|
|
39583
39735
|
const provisionalSignals = allFindings.filter((f) => !alreadySignaled.has(f.originalAgentId)).map((f) => ({
|
|
39584
39736
|
type: "consensus",
|
|
39585
|
-
taskId:
|
|
39737
|
+
taskId: provisionalConsensusId || "",
|
|
39586
39738
|
consensusId: provisionalConsensusId,
|
|
39587
39739
|
findingId: typeof f.id === "string" ? f.id : void 0,
|
|
39588
39740
|
signal: tagToSignal[f.tag] || "unique_unconfirmed",
|
|
@@ -39958,6 +40110,7 @@ var planExecutionDepth = 0;
|
|
|
39958
40110
|
var _pendingSessionData = /* @__PURE__ */ new Map();
|
|
39959
40111
|
var _pendingSkillData = /* @__PURE__ */ new Map();
|
|
39960
40112
|
var _pendingVerifyData = /* @__PURE__ */ new Map();
|
|
40113
|
+
var _pendingPlanData = /* @__PURE__ */ new Map();
|
|
39961
40114
|
var _modules = null;
|
|
39962
40115
|
function lookupFindingSeverity(findingId, projectRoot) {
|
|
39963
40116
|
const { existsSync: existsSync44, readdirSync: readdirSync13, readFileSync: readFileSync41 } = require("fs");
|
|
@@ -40098,16 +40251,16 @@ async function doBoot() {
|
|
|
40098
40251
|
}
|
|
40099
40252
|
const perfWriter = new m.PerformanceWriter(process.cwd());
|
|
40100
40253
|
const memorySearcher = new m.MemorySearcher(process.cwd());
|
|
40101
|
-
|
|
40254
|
+
ctx.identityRegistry.clear();
|
|
40102
40255
|
for (const a of agentConfigs) {
|
|
40103
|
-
identityRegistry.set(a.id, {
|
|
40256
|
+
ctx.identityRegistry.set(a.id, {
|
|
40104
40257
|
agent_id: a.id,
|
|
40105
40258
|
runtime: a.native ? "native" : "relay",
|
|
40106
40259
|
provider: a.provider,
|
|
40107
40260
|
model: a.model
|
|
40108
40261
|
});
|
|
40109
40262
|
}
|
|
40110
|
-
const agentLookup = (id) => identityRegistry.get(id);
|
|
40263
|
+
const agentLookup = (id) => ctx.identityRegistry.get(id);
|
|
40111
40264
|
ctx.toolServer = new m.ToolServer({
|
|
40112
40265
|
relayUrl: ctx.relay.url,
|
|
40113
40266
|
projectRoot: process.cwd(),
|
|
@@ -40142,7 +40295,7 @@ async function doBoot() {
|
|
|
40142
40295
|
const { join: join51 } = require("path");
|
|
40143
40296
|
const instructionsPath = join51(process.cwd(), ".gossip", "agents", ac.id, "instructions.md");
|
|
40144
40297
|
const baseInstructions = existsSync44(instructionsPath) ? readFileSync41(instructionsPath, "utf-8") : "";
|
|
40145
|
-
const identity = identityRegistry.get(ac.id);
|
|
40298
|
+
const identity = ctx.identityRegistry.get(ac.id);
|
|
40146
40299
|
const identityBlock = identity ? m.formatIdentityBlock(identity) + "\n" : "";
|
|
40147
40300
|
const instructions = (identityBlock + baseInstructions).trim() || void 0;
|
|
40148
40301
|
const enableWebSearch = ac.preset === "researcher" || (ac.skills ?? []).includes("research");
|
|
@@ -40171,10 +40324,13 @@ async function doBoot() {
|
|
|
40171
40324
|
agentConfigs.push(ac);
|
|
40172
40325
|
const modelTier = sa.model.includes("opus") ? "opus" : sa.model.includes("haiku") ? "haiku" : "sonnet";
|
|
40173
40326
|
ctx.nativeAgentConfigs.set(ac.id, { model: modelTier, instructions: sa.instructions, description: sa.description, skills: ac.skills || [] });
|
|
40327
|
+
ctx.identityRegistry.set(ac.id, { agent_id: ac.id, runtime: "native", provider: ac.provider, model: ac.model });
|
|
40174
40328
|
process.stderr.write(`[gossipcat] \u{1F916} Registered native agent: ${sa.id} (${modelTier})
|
|
40175
40329
|
`);
|
|
40176
40330
|
}
|
|
40177
40331
|
}
|
|
40332
|
+
ctx.mainProviderConfig = config2.main_agent.provider;
|
|
40333
|
+
ctx.mainModelConfig = config2.main_agent.model;
|
|
40178
40334
|
let mainProvider = config2.main_agent.provider;
|
|
40179
40335
|
let mainModel = config2.main_agent.model;
|
|
40180
40336
|
let mainKey = null;
|
|
@@ -40209,6 +40365,7 @@ async function doBoot() {
|
|
|
40209
40365
|
}
|
|
40210
40366
|
}
|
|
40211
40367
|
ctx.mainProvider = mainProvider;
|
|
40368
|
+
ctx.mainModel = mainModel;
|
|
40212
40369
|
const supaKey = await ctx.keychain.getKey("supabase");
|
|
40213
40370
|
const supaTeamSalt = await ctx.keychain.getKey("supabase-team-salt");
|
|
40214
40371
|
ctx.mainAgent = new m.MainAgent({
|
|
@@ -40426,8 +40583,23 @@ async function doSyncWorkers() {
|
|
|
40426
40583
|
if (!configPath) return;
|
|
40427
40584
|
const config2 = m.loadConfig(configPath);
|
|
40428
40585
|
const agentConfigs = m.configToAgentConfigs(config2);
|
|
40586
|
+
if (config2.main_agent && (config2.main_agent.provider !== ctx.mainProviderConfig || config2.main_agent.model !== ctx.mainModelConfig)) {
|
|
40587
|
+
process.stderr.write(
|
|
40588
|
+
`[gossipcat] \u26A0 main_agent changed in config: ${ctx.mainProviderConfig}/${ctx.mainModelConfig} \u2192 ${config2.main_agent.provider}/${config2.main_agent.model}. Restart Claude Code (/mcp reconnect) for the new orchestrator LLM to take effect.
|
|
40589
|
+
`
|
|
40590
|
+
);
|
|
40591
|
+
ctx.mainProviderConfig = config2.main_agent.provider;
|
|
40592
|
+
ctx.mainModelConfig = config2.main_agent.model;
|
|
40593
|
+
}
|
|
40594
|
+
ctx.identityRegistry.clear();
|
|
40429
40595
|
for (const ac of agentConfigs) {
|
|
40430
40596
|
ctx.mainAgent.registerAgent(ac);
|
|
40597
|
+
ctx.identityRegistry.set(ac.id, {
|
|
40598
|
+
agent_id: ac.id,
|
|
40599
|
+
runtime: ac.native ? "native" : "relay",
|
|
40600
|
+
provider: ac.provider,
|
|
40601
|
+
model: ac.model
|
|
40602
|
+
});
|
|
40431
40603
|
if (ac.native && !ctx.nativeAgentConfigs.has(ac.id)) {
|
|
40432
40604
|
const { existsSync: ex, readFileSync: rf } = require("fs");
|
|
40433
40605
|
const { join: j } = require("path");
|
|
@@ -40453,6 +40625,7 @@ async function doSyncWorkers() {
|
|
|
40453
40625
|
ctx.mainAgent.registerAgent(ac);
|
|
40454
40626
|
const modelTier = sa.model.includes("opus") ? "opus" : sa.model.includes("haiku") ? "haiku" : "sonnet";
|
|
40455
40627
|
ctx.nativeAgentConfigs.set(ac.id, { model: modelTier, instructions: sa.instructions, description: sa.description, skills: ac.skills || [] });
|
|
40628
|
+
ctx.identityRegistry.set(ac.id, { agent_id: ac.id, runtime: "native", provider: ac.provider, model: ac.model });
|
|
40456
40629
|
}
|
|
40457
40630
|
}
|
|
40458
40631
|
const added = await ctx.mainAgent.syncWorkers((provider) => ctx.keychain.getKey(provider));
|
|
@@ -40467,6 +40640,25 @@ async function doSyncWorkers() {
|
|
|
40467
40640
|
process.stderr.write(`[gossipcat] \u{1F504} Synced: ${ctx.workers.size} relay workers + ${ctx.nativeAgentConfigs.size} native agents
|
|
40468
40641
|
`);
|
|
40469
40642
|
}
|
|
40643
|
+
try {
|
|
40644
|
+
const skillIndex = ctx.mainAgent.getSkillIndex?.();
|
|
40645
|
+
if (skillIndex) {
|
|
40646
|
+
const merged = [...agentConfigs, ...claudeSubagentsToConfigs2(claudeSubagents)];
|
|
40647
|
+
skillIndex.seedFromConfigs(merged.map((ac) => ({ id: ac.id, skills: ac.skills || [] })));
|
|
40648
|
+
const allIds = merged.map((ac) => ac.id).filter((id) => typeof id === "string" && id.length > 0);
|
|
40649
|
+
if (allIds.length > 0) skillIndex.ensureBoundWithMode(["memory-retrieval"], allIds, "permanent");
|
|
40650
|
+
}
|
|
40651
|
+
} catch {
|
|
40652
|
+
}
|
|
40653
|
+
try {
|
|
40654
|
+
ctx.mainAgent.invalidateProjectStructureCache?.();
|
|
40655
|
+
} catch {
|
|
40656
|
+
}
|
|
40657
|
+
try {
|
|
40658
|
+
const merged = [...agentConfigs, ...claudeSubagentsToConfigs2(claudeSubagents)];
|
|
40659
|
+
ctx.relay?.setAgentConfigs(merged);
|
|
40660
|
+
} catch {
|
|
40661
|
+
}
|
|
40470
40662
|
} catch (err) {
|
|
40471
40663
|
process.stderr.write(`[gossipcat] \u274C syncWorkers failed: ${err.message}
|
|
40472
40664
|
`);
|
|
@@ -40484,17 +40676,83 @@ var server = new import_mcp.McpServer(
|
|
|
40484
40676
|
instructions: "gossipcat \u2014 multi-agent orchestration. ALWAYS call gossip_status() first when starting work in this project. The gossip_status response loads the orchestrator role, dispatch rules, consensus workflow, native agent relay rule, sandbox enforcement, and other operating rules from .gossip/rules.md. These rules are not in this instruction text \u2014 they live in the gossip_status output to keep the instruction surface small and to allow per-project customization."
|
|
40485
40677
|
}
|
|
40486
40678
|
);
|
|
40679
|
+
function buildPlanResponseText(args) {
|
|
40680
|
+
const { task, plan, planned, planId } = args;
|
|
40681
|
+
const taskLines = planned.map((t, i) => {
|
|
40682
|
+
const tag = t.access === "write" ? "[WRITE]" : "[READ]";
|
|
40683
|
+
let line = ` ${i + 1}. ${tag} ${t.agentId || "unassigned"} \u2192 "${t.task}"`;
|
|
40684
|
+
if (t.writeMode) {
|
|
40685
|
+
line += `
|
|
40686
|
+
write_mode: ${t.writeMode}`;
|
|
40687
|
+
if (t.scope) line += ` | scope: ${t.scope}`;
|
|
40688
|
+
}
|
|
40689
|
+
return line;
|
|
40690
|
+
}).join("\n");
|
|
40691
|
+
const assignedTasks = planned.filter((t) => t.agentId);
|
|
40692
|
+
const unassignedTasks = planned.filter((t) => !t.agentId);
|
|
40693
|
+
const planJson = {
|
|
40694
|
+
strategy: plan.strategy,
|
|
40695
|
+
tasks: assignedTasks.map((t, i) => {
|
|
40696
|
+
const entry = { agent_id: t.agentId, task: t.task };
|
|
40697
|
+
if (t.writeMode) entry.write_mode = t.writeMode;
|
|
40698
|
+
if (t.scope) entry.scope = t.scope;
|
|
40699
|
+
entry.plan_id = planId;
|
|
40700
|
+
entry.step = i + 1;
|
|
40701
|
+
return entry;
|
|
40702
|
+
})
|
|
40703
|
+
};
|
|
40704
|
+
let warnings = "";
|
|
40705
|
+
if (plan.warnings?.length) {
|
|
40706
|
+
warnings = `
|
|
40707
|
+
Warnings:
|
|
40708
|
+
${plan.warnings.map((w) => ` - ${w}`).join("\n")}
|
|
40709
|
+
`;
|
|
40710
|
+
}
|
|
40711
|
+
if (unassignedTasks.length) {
|
|
40712
|
+
warnings += `
|
|
40713
|
+
Unassigned (excluded from PLAN_JSON \u2014 no matching agent):
|
|
40714
|
+
${unassignedTasks.map((t) => ` - "${t.task}"`).join("\n")}
|
|
40715
|
+
`;
|
|
40716
|
+
}
|
|
40717
|
+
let dispatchBlock;
|
|
40718
|
+
if (plan.strategy === "sequential" || plan.strategy === "single") {
|
|
40719
|
+
const steps = planJson.tasks.map((t, i) => {
|
|
40720
|
+
const args2 = [`agent_id: "${t.agent_id}"`, `task: "${t.task}"`];
|
|
40721
|
+
if (t.write_mode) args2.push(`write_mode: "${t.write_mode}"`);
|
|
40722
|
+
if (t.scope) args2.push(`scope: "${t.scope}"`);
|
|
40723
|
+
args2.push(`plan_id: "${planId}"`, `step: ${i + 1}`);
|
|
40724
|
+
return `Step ${i + 1}: gossip_dispatch(${args2.join(", ")})
|
|
40725
|
+
then: gossip_collect()`;
|
|
40726
|
+
});
|
|
40727
|
+
dispatchBlock = `Execute sequentially:
|
|
40728
|
+
${steps.join("\n\n")}`;
|
|
40729
|
+
} else {
|
|
40730
|
+
dispatchBlock = `PLAN_JSON (pass to gossip_dispatch with mode:"parallel"):
|
|
40731
|
+
${JSON.stringify(planJson)}`;
|
|
40732
|
+
}
|
|
40733
|
+
return `Plan: "${task}"
|
|
40734
|
+
Plan ID: ${planId}
|
|
40735
|
+
|
|
40736
|
+
Strategy: ${plan.strategy}
|
|
40737
|
+
|
|
40738
|
+
Tasks:
|
|
40739
|
+
${taskLines}
|
|
40740
|
+
${warnings}
|
|
40741
|
+
---
|
|
40742
|
+
${dispatchBlock}`;
|
|
40743
|
+
}
|
|
40487
40744
|
server.tool(
|
|
40488
40745
|
"gossip_plan",
|
|
40489
40746
|
'Plan a task with write-mode suggestions. Decomposes into sub-tasks, assigns agents, and classifies each as read or write with suggested write mode. Returns dispatch-ready JSON for approval before execution. Use this before gossip_dispatch(mode:"parallel") for implementation tasks.',
|
|
40490
40747
|
{
|
|
40491
40748
|
task: external_exports.string().describe('Task description (e.g. "fix the scope validation bug in packages/tools/")'),
|
|
40492
|
-
strategy: external_exports.enum(["parallel", "sequential", "single"]).optional().describe("Override decomposition strategy. Omit to let the orchestrator decide.")
|
|
40749
|
+
strategy: external_exports.enum(["parallel", "sequential", "single"]).optional().describe("Override decomposition strategy. Omit to let the orchestrator decide."),
|
|
40750
|
+
_utility_task_id: external_exports.string().optional().describe("Internal: utility task ID for re-entry after native decomposition")
|
|
40493
40751
|
},
|
|
40494
|
-
async ({ task, strategy }) => {
|
|
40752
|
+
async ({ task, strategy, _utility_task_id }) => {
|
|
40495
40753
|
await boot();
|
|
40496
40754
|
await syncWorkersViaKeychain();
|
|
40497
|
-
if (planExecutionDepth > 0) {
|
|
40755
|
+
if (planExecutionDepth > 0 && !_utility_task_id) {
|
|
40498
40756
|
return { content: [{ type: "text", text: "Skipped: already inside a plan step. Execute the task directly instead of re-planning." }] };
|
|
40499
40757
|
}
|
|
40500
40758
|
try {
|
|
@@ -40507,6 +40765,46 @@ server.tool(
|
|
|
40507
40765
|
const registry2 = new AgentRegistry2();
|
|
40508
40766
|
for (const ac of agentConfigs) registry2.register(ac);
|
|
40509
40767
|
const { createProvider: createProvider2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
|
|
40768
|
+
if (_utility_task_id) {
|
|
40769
|
+
const stashed = _pendingPlanData.get(_utility_task_id);
|
|
40770
|
+
if (!stashed) {
|
|
40771
|
+
return { content: [{ type: "text", text: `Plan error: no stashed data for utility task ${_utility_task_id}. Re-run gossip_plan.` }] };
|
|
40772
|
+
}
|
|
40773
|
+
const utilityResult = ctx.nativeResultMap.get(_utility_task_id);
|
|
40774
|
+
_pendingPlanData.delete(_utility_task_id);
|
|
40775
|
+
ctx.nativeResultMap.delete(_utility_task_id);
|
|
40776
|
+
ctx.nativeTaskMap.delete(_utility_task_id);
|
|
40777
|
+
if (!utilityResult || utilityResult.status !== "completed" || !utilityResult.result) {
|
|
40778
|
+
process.stderr.write(`[gossipcat] gossip_plan native utility ${_utility_task_id} failed/timed out
|
|
40779
|
+
`);
|
|
40780
|
+
return { content: [{ type: "text", text: `Plan error: native decomposition failed or timed out. Configure an API key (gossipcat setup) and retry.` }] };
|
|
40781
|
+
}
|
|
40782
|
+
const dispatcher2 = new TaskDispatcher2(null, registry2);
|
|
40783
|
+
const plan2 = dispatcher2.decomposeFromRaw(stashed.task, utilityResult.result);
|
|
40784
|
+
if (stashed.strategy) plan2.strategy = stashed.strategy;
|
|
40785
|
+
dispatcher2.assignAgents(plan2);
|
|
40786
|
+
const planned2 = dispatcher2.classifyWriteModesFallback(plan2);
|
|
40787
|
+
const planId2 = (0, import_crypto20.randomUUID)().slice(0, 8);
|
|
40788
|
+
const assignedTasks2 = planned2.filter((t) => t.agentId);
|
|
40789
|
+
const planState2 = {
|
|
40790
|
+
id: planId2,
|
|
40791
|
+
task: stashed.task,
|
|
40792
|
+
strategy: plan2.strategy,
|
|
40793
|
+
steps: assignedTasks2.map((t, i) => ({
|
|
40794
|
+
step: i + 1,
|
|
40795
|
+
agentId: t.agentId,
|
|
40796
|
+
task: t.task,
|
|
40797
|
+
writeMode: t.writeMode,
|
|
40798
|
+
scope: t.scope
|
|
40799
|
+
})),
|
|
40800
|
+
createdAt: Date.now()
|
|
40801
|
+
};
|
|
40802
|
+
ctx.mainAgent.registerPlan(planState2);
|
|
40803
|
+
const text2 = buildPlanResponseText({ task: stashed.task, plan: plan2, planned: planned2, planId: planId2 });
|
|
40804
|
+
return { content: [{ type: "text", text: `${text2}
|
|
40805
|
+
|
|
40806
|
+
Note: write-mode classification unavailable on this native-only install \u2014 all tasks default to read. Configure a relay API key to enable full classification.` }] };
|
|
40807
|
+
}
|
|
40510
40808
|
let llm;
|
|
40511
40809
|
const mainKey = await ctx.keychain.getKey(config2.main_agent.provider);
|
|
40512
40810
|
if (mainKey) {
|
|
@@ -40521,26 +40819,55 @@ server.tool(
|
|
|
40521
40819
|
break;
|
|
40522
40820
|
}
|
|
40523
40821
|
}
|
|
40524
|
-
if (!llm)
|
|
40822
|
+
if (!llm) {
|
|
40823
|
+
if (ctx.nativeUtilityConfig) {
|
|
40824
|
+
const utilityTaskId = (0, import_crypto20.randomUUID)().slice(0, 8);
|
|
40825
|
+
const relayToken = (0, import_crypto20.randomUUID)().slice(0, 12);
|
|
40826
|
+
const dispatcher2 = new TaskDispatcher2(null, registry2);
|
|
40827
|
+
const messages = dispatcher2.buildDecomposeMessages(task);
|
|
40828
|
+
const asString = (c) => typeof c === "string" ? c : Array.isArray(c) ? c.map((x) => typeof x === "string" ? x : x?.text ?? "").join("") : "";
|
|
40829
|
+
const system = asString(messages.find((m) => m.role === "system")?.content);
|
|
40830
|
+
const user = asString(messages.find((m) => m.role === "user")?.content);
|
|
40831
|
+
_pendingPlanData.set(utilityTaskId, { task, strategy });
|
|
40832
|
+
const UTILITY_TTL_MS = 12e4;
|
|
40833
|
+
ctx.nativeTaskMap.set(utilityTaskId, {
|
|
40834
|
+
agentId: "_utility",
|
|
40835
|
+
task: `plan:${task.slice(0, 120)}`,
|
|
40836
|
+
startedAt: Date.now(),
|
|
40837
|
+
timeoutMs: UTILITY_TTL_MS,
|
|
40838
|
+
utilityType: "plan",
|
|
40839
|
+
relayToken
|
|
40840
|
+
});
|
|
40841
|
+
spawnTimeoutWatcher(utilityTaskId, ctx.nativeTaskMap.get(utilityTaskId));
|
|
40842
|
+
const STASH_TTL_MS = UTILITY_TTL_MS + 3e4;
|
|
40843
|
+
setTimeout(() => {
|
|
40844
|
+
_pendingPlanData.delete(utilityTaskId);
|
|
40845
|
+
}, STASH_TTL_MS).unref();
|
|
40846
|
+
const { assembleUtilityPrompt: assembleUtilityPrompt2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
|
|
40847
|
+
const modelShort = ctx.nativeUtilityConfig.model;
|
|
40848
|
+
return {
|
|
40849
|
+
content: assembleUtilityPrompt2({
|
|
40850
|
+
taskId: utilityTaskId,
|
|
40851
|
+
modelShort,
|
|
40852
|
+
system,
|
|
40853
|
+
user,
|
|
40854
|
+
relayToken,
|
|
40855
|
+
intro: "No API keys available. Dispatching native utility for decomposition.",
|
|
40856
|
+
// F19: JSON.stringify handles backslashes + newlines + quotes correctly.
|
|
40857
|
+
reentrantCall: `gossip_plan(task: ${JSON.stringify(task)}, _utility_task_id: "${utilityTaskId}")`
|
|
40858
|
+
})
|
|
40859
|
+
};
|
|
40860
|
+
}
|
|
40861
|
+
return { content: [{ type: "text", text: "No API keys available. Run gossipcat setup to configure keys." }] };
|
|
40862
|
+
}
|
|
40525
40863
|
}
|
|
40526
40864
|
const dispatcher = new TaskDispatcher2(llm, registry2);
|
|
40527
40865
|
const plan = await dispatcher.decompose(task);
|
|
40528
40866
|
if (strategy) plan.strategy = strategy;
|
|
40529
40867
|
dispatcher.assignAgents(plan);
|
|
40530
40868
|
const planned = await dispatcher.classifyWriteModes(plan);
|
|
40531
|
-
const taskLines = planned.map((t, i) => {
|
|
40532
|
-
const tag = t.access === "write" ? "[WRITE]" : "[READ]";
|
|
40533
|
-
let line = ` ${i + 1}. ${tag} ${t.agentId || "unassigned"} \u2192 "${t.task}"`;
|
|
40534
|
-
if (t.writeMode) {
|
|
40535
|
-
line += `
|
|
40536
|
-
write_mode: ${t.writeMode}`;
|
|
40537
|
-
if (t.scope) line += ` | scope: ${t.scope}`;
|
|
40538
|
-
}
|
|
40539
|
-
return line;
|
|
40540
|
-
}).join("\n");
|
|
40541
|
-
const assignedTasks = planned.filter((t) => t.agentId);
|
|
40542
|
-
const unassignedTasks = planned.filter((t) => !t.agentId);
|
|
40543
40869
|
const planId = (0, import_crypto20.randomUUID)().slice(0, 8);
|
|
40870
|
+
const assignedTasks = planned.filter((t) => t.agentId);
|
|
40544
40871
|
const planState = {
|
|
40545
40872
|
id: planId,
|
|
40546
40873
|
task,
|
|
@@ -40555,56 +40882,7 @@ server.tool(
|
|
|
40555
40882
|
createdAt: Date.now()
|
|
40556
40883
|
};
|
|
40557
40884
|
ctx.mainAgent.registerPlan(planState);
|
|
40558
|
-
const
|
|
40559
|
-
strategy: plan.strategy,
|
|
40560
|
-
tasks: assignedTasks.map((t, i) => {
|
|
40561
|
-
const entry = { agent_id: t.agentId, task: t.task };
|
|
40562
|
-
if (t.writeMode) entry.write_mode = t.writeMode;
|
|
40563
|
-
if (t.scope) entry.scope = t.scope;
|
|
40564
|
-
entry.plan_id = planId;
|
|
40565
|
-
entry.step = i + 1;
|
|
40566
|
-
return entry;
|
|
40567
|
-
})
|
|
40568
|
-
};
|
|
40569
|
-
let warnings = "";
|
|
40570
|
-
if (plan.warnings?.length) {
|
|
40571
|
-
warnings = `
|
|
40572
|
-
Warnings:
|
|
40573
|
-
${plan.warnings.map((w) => ` - ${w}`).join("\n")}
|
|
40574
|
-
`;
|
|
40575
|
-
}
|
|
40576
|
-
if (unassignedTasks.length) {
|
|
40577
|
-
warnings += `
|
|
40578
|
-
Unassigned (excluded from PLAN_JSON \u2014 no matching agent):
|
|
40579
|
-
${unassignedTasks.map((t) => ` - "${t.task}"`).join("\n")}
|
|
40580
|
-
`;
|
|
40581
|
-
}
|
|
40582
|
-
let dispatchBlock;
|
|
40583
|
-
if (plan.strategy === "sequential" || plan.strategy === "single") {
|
|
40584
|
-
const steps = planJson.tasks.map((t, i) => {
|
|
40585
|
-
const args = [`agent_id: "${t.agent_id}"`, `task: "${t.task}"`];
|
|
40586
|
-
if (t.write_mode) args.push(`write_mode: "${t.write_mode}"`);
|
|
40587
|
-
if (t.scope) args.push(`scope: "${t.scope}"`);
|
|
40588
|
-
args.push(`plan_id: "${planId}"`, `step: ${i + 1}`);
|
|
40589
|
-
return `Step ${i + 1}: gossip_dispatch(${args.join(", ")})
|
|
40590
|
-
then: gossip_collect()`;
|
|
40591
|
-
});
|
|
40592
|
-
dispatchBlock = `Execute sequentially:
|
|
40593
|
-
${steps.join("\n\n")}`;
|
|
40594
|
-
} else {
|
|
40595
|
-
dispatchBlock = `PLAN_JSON (pass to gossip_dispatch with mode:"parallel"):
|
|
40596
|
-
${JSON.stringify(planJson)}`;
|
|
40597
|
-
}
|
|
40598
|
-
const text = `Plan: "${task}"
|
|
40599
|
-
Plan ID: ${planId}
|
|
40600
|
-
|
|
40601
|
-
Strategy: ${plan.strategy}
|
|
40602
|
-
|
|
40603
|
-
Tasks:
|
|
40604
|
-
${taskLines}
|
|
40605
|
-
${warnings}
|
|
40606
|
-
---
|
|
40607
|
-
${dispatchBlock}`;
|
|
40885
|
+
const text = buildPlanResponseText({ task, plan, planned, planId });
|
|
40608
40886
|
return { content: [{ type: "text", text }] };
|
|
40609
40887
|
} catch (err) {
|
|
40610
40888
|
return { content: [{ type: "text", text: `Plan error: ${err.message}` }] };
|
|
@@ -41155,6 +41433,12 @@ server.tool(
|
|
|
41155
41433
|
}
|
|
41156
41434
|
mkdirSync22(join51(root, ".gossip"), { recursive: true });
|
|
41157
41435
|
writeFileSync18(join51(root, ".gossip", "config.json"), JSON.stringify(config2, null, 2));
|
|
41436
|
+
try {
|
|
41437
|
+
await syncWorkersViaKeychain();
|
|
41438
|
+
} catch (e) {
|
|
41439
|
+
process.stderr.write(`[gossipcat] gossip_setup: failed to refresh agent state: ${e}
|
|
41440
|
+
`);
|
|
41441
|
+
}
|
|
41158
41442
|
const agentList = Object.entries(config2.agents).map(([id, a]) => `- ${id}: ${a.provider}/${a.model} (${a.preset || "custom"})${a.native ? " \u2014 native" : ""}`).join("\n");
|
|
41159
41443
|
const rulesDir = join51(root, env.rulesDir);
|
|
41160
41444
|
const rulesFile = join51(root, env.rulesFile);
|
|
@@ -41266,102 +41550,12 @@ Then review the plan and dispatch with gossip_dispatch(mode: "parallel", tasks:
|
|
|
41266
41550
|
}
|
|
41267
41551
|
}
|
|
41268
41552
|
const isNative = ctx.nativeAgentConfigs.has(agent_id);
|
|
41269
|
-
|
|
41270
|
-
|
|
41271
|
-
const { relativizeProjectPaths: relativizeProjectPaths2, readSandboxMode: readSandboxMode2, shouldSanitize: shouldSanitize2 } = (init_sandbox2(), __toCommonJS(sandbox_exports));
|
|
41272
|
-
if (readSandboxMode2(process.cwd()) !== "off") {
|
|
41273
|
-
const preset = (() => {
|
|
41274
|
-
try {
|
|
41275
|
-
return ctx.mainAgent.getAgentList?.().find((a) => a.id === agent_id)?.preset;
|
|
41276
|
-
} catch {
|
|
41277
|
-
return void 0;
|
|
41278
|
-
}
|
|
41279
|
-
})();
|
|
41280
|
-
if (shouldSanitize2(write_mode, preset)) {
|
|
41281
|
-
const { sanitized, replacements } = relativizeProjectPaths2(task, process.cwd());
|
|
41282
|
-
if (replacements > 0) {
|
|
41283
|
-
process.stderr.write(`[gossipcat] \u{1F9F9} sanitized ${replacements} project path(s) in task for ${agent_id}
|
|
41284
|
-
`);
|
|
41285
|
-
}
|
|
41286
|
-
task = sanitized;
|
|
41287
|
-
_runSanitized = true;
|
|
41288
|
-
}
|
|
41289
|
-
}
|
|
41290
|
-
} catch {
|
|
41553
|
+
if (isNative) {
|
|
41554
|
+
return handleDispatchSingle(agent_id, task, write_mode, scope);
|
|
41291
41555
|
}
|
|
41292
41556
|
const options = {};
|
|
41293
41557
|
if (write_mode) options.writeMode = write_mode;
|
|
41294
41558
|
if (scope) options.scope = scope;
|
|
41295
|
-
if (isNative) {
|
|
41296
|
-
if (write_mode === "scoped") {
|
|
41297
|
-
if (!scope) {
|
|
41298
|
-
return { content: [{ type: "text", text: "Error: scoped write mode requires a scope path" }] };
|
|
41299
|
-
}
|
|
41300
|
-
const overlap = ctx.mainAgent.scopeTracker.hasOverlap(scope);
|
|
41301
|
-
if (overlap.overlaps) {
|
|
41302
|
-
return { content: [{ type: "text", text: `Error: Scope "${scope}" conflicts with running task ${overlap.conflictTaskId} at "${overlap.conflictScope}"` }] };
|
|
41303
|
-
}
|
|
41304
|
-
}
|
|
41305
|
-
evictStaleNativeTasks();
|
|
41306
|
-
const taskId = require("crypto").randomUUID().slice(0, 8);
|
|
41307
|
-
const relayToken = require("crypto").randomUUID().slice(0, 12);
|
|
41308
|
-
ctx.nativeTaskMap.set(taskId, { agentId: agent_id, task, startedAt: Date.now(), timeoutMs: NATIVE_TASK_TTL_MS, relayToken });
|
|
41309
|
-
spawnTimeoutWatcher(taskId, ctx.nativeTaskMap.get(taskId));
|
|
41310
|
-
persistNativeTaskMap();
|
|
41311
|
-
try {
|
|
41312
|
-
ctx.mainAgent.recordNativeTask(taskId, agent_id, task);
|
|
41313
|
-
} catch {
|
|
41314
|
-
}
|
|
41315
|
-
if (write_mode === "scoped" && scope) {
|
|
41316
|
-
ctx.mainAgent.scopeTracker.register(scope, taskId);
|
|
41317
|
-
}
|
|
41318
|
-
const config2 = ctx.nativeAgentConfigs.get(agent_id);
|
|
41319
|
-
const basePrompt = config2.instructions || `You are a skilled ${config2.description || "agent"}. Complete the task thoroughly.`;
|
|
41320
|
-
const scopePrefix = write_mode === "scoped" && scope ? `SCOPE RESTRICTION: Only modify files within ${scope}. Do not edit files outside this directory.
|
|
41321
|
-
|
|
41322
|
-
` : "";
|
|
41323
|
-
let agentPrompt = `${scopePrefix}${basePrompt}
|
|
41324
|
-
|
|
41325
|
-
---
|
|
41326
|
-
|
|
41327
|
-
Task: ${task}`;
|
|
41328
|
-
if (_runSanitized) {
|
|
41329
|
-
try {
|
|
41330
|
-
const { prependScopeNote: prependScopeNote2 } = (init_sandbox2(), __toCommonJS(sandbox_exports));
|
|
41331
|
-
agentPrompt = prependScopeNote2(agentPrompt);
|
|
41332
|
-
} catch {
|
|
41333
|
-
}
|
|
41334
|
-
}
|
|
41335
|
-
try {
|
|
41336
|
-
const { recordDispatchMetadata: recordDispatchMetadata2 } = (init_sandbox2(), __toCommonJS(sandbox_exports));
|
|
41337
|
-
recordDispatchMetadata2(process.cwd(), {
|
|
41338
|
-
taskId,
|
|
41339
|
-
agentId: agent_id,
|
|
41340
|
-
writeMode: write_mode,
|
|
41341
|
-
scope,
|
|
41342
|
-
timestamp: Date.now()
|
|
41343
|
-
});
|
|
41344
|
-
} catch {
|
|
41345
|
-
}
|
|
41346
|
-
const modelShort = config2.model || "sonnet";
|
|
41347
|
-
return {
|
|
41348
|
-
content: [
|
|
41349
|
-
{
|
|
41350
|
-
type: "text",
|
|
41351
|
-
text: `Dispatched to ${agent_id} (native). Task ID: ${taskId}
|
|
41352
|
-
|
|
41353
|
-
\u26A0\uFE0F EXECUTE NOW \u2014 launch this Agent and relay the result:
|
|
41354
|
-
|
|
41355
|
-
1. Agent(model: "${modelShort}", prompt: <AGENT_PROMPT:${taskId} below>, run_in_background: true) \u2014 pass the AGENT_PROMPT:${taskId} content item verbatim
|
|
41356
|
-
2. When agent completes \u2192 gossip_relay(task_id: "${taskId}", relay_token: "${relayToken}", result: "<full agent output>")
|
|
41357
|
-
|
|
41358
|
-
Do BOTH steps in your next response. Do not wait for user input between them.`
|
|
41359
|
-
},
|
|
41360
|
-
{ type: "text", text: `AGENT_PROMPT:${taskId} (${agent_id})
|
|
41361
|
-
${agentPrompt}` }
|
|
41362
|
-
]
|
|
41363
|
-
};
|
|
41364
|
-
}
|
|
41365
41559
|
if (!ctx.workers.has(agent_id)) {
|
|
41366
41560
|
await syncWorkersViaKeychain();
|
|
41367
41561
|
}
|
package/package.json
CHANGED