gossipcat 0.1.0 → 0.2.0
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 +294 -121
- package/dist-dashboard/assets/index-Dx4sBGgb.js +65 -0
- package/dist-dashboard/assets/index-rAO4S2u5.css +1 -0
- package/dist-dashboard/index.html +2 -2
- package/dist-mcp/data/archetypes.json +226 -0
- package/dist-mcp/mcp-server.js +1331 -841
- package/package.json +2 -2
- package/dist-dashboard/assets/index-BvqFkH-m.css +0 -1
- package/dist-dashboard/assets/index-Dsv-K6u_.js +0 -65
package/dist-mcp/mcp-server.js
CHANGED
|
@@ -55,6 +55,9 @@ var init_mcp_context = __esm({
|
|
|
55
55
|
pendingConsensusRounds: /* @__PURE__ */ new Map(),
|
|
56
56
|
nativeUtilityConfig: null,
|
|
57
57
|
mainProvider: "google",
|
|
58
|
+
httpMcpPort: null,
|
|
59
|
+
relayPortSource: null,
|
|
60
|
+
httpMcpPortSource: null,
|
|
58
61
|
booted: false,
|
|
59
62
|
boot: async () => {
|
|
60
63
|
throw new Error("boot not initialized");
|
|
@@ -69,6 +72,46 @@ var init_mcp_context = __esm({
|
|
|
69
72
|
}
|
|
70
73
|
});
|
|
71
74
|
|
|
75
|
+
// apps/cli/src/version.ts
|
|
76
|
+
var version_exports = {};
|
|
77
|
+
__export(version_exports, {
|
|
78
|
+
getGossipcatVersion: () => getGossipcatVersion
|
|
79
|
+
});
|
|
80
|
+
function getGossipcatVersion() {
|
|
81
|
+
if (cached2 !== null) return cached2;
|
|
82
|
+
cached2 = resolveVersion();
|
|
83
|
+
return cached2;
|
|
84
|
+
}
|
|
85
|
+
function resolveVersion() {
|
|
86
|
+
let dir = __dirname;
|
|
87
|
+
const root = (0, import_path.resolve)("/");
|
|
88
|
+
for (let i = 0; i < 20 && dir !== root; i++) {
|
|
89
|
+
const candidate = (0, import_path.resolve)(dir, "package.json");
|
|
90
|
+
if ((0, import_fs.existsSync)(candidate)) {
|
|
91
|
+
try {
|
|
92
|
+
const pkg = JSON.parse((0, import_fs.readFileSync)(candidate, "utf-8"));
|
|
93
|
+
if (pkg && pkg.name === "gossipcat" && typeof pkg.version === "string") {
|
|
94
|
+
return pkg.version;
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const parent = (0, import_path.dirname)(dir);
|
|
100
|
+
if (parent === dir) break;
|
|
101
|
+
dir = parent;
|
|
102
|
+
}
|
|
103
|
+
return "unknown";
|
|
104
|
+
}
|
|
105
|
+
var import_fs, import_path, cached2;
|
|
106
|
+
var init_version = __esm({
|
|
107
|
+
"apps/cli/src/version.ts"() {
|
|
108
|
+
"use strict";
|
|
109
|
+
import_fs = require("fs");
|
|
110
|
+
import_path = require("path");
|
|
111
|
+
cached2 = null;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
72
115
|
// packages/orchestrator/src/llm-client.ts
|
|
73
116
|
async function fetchWithRetry503(url2, init, providerName) {
|
|
74
117
|
const res = await fetch(url2, init);
|
|
@@ -108,13 +151,13 @@ function createProvider(provider, model, apiKey, projectRoot, baseUrl) {
|
|
|
108
151
|
throw new Error(`Unknown provider: ${provider}`);
|
|
109
152
|
}
|
|
110
153
|
}
|
|
111
|
-
var import_crypto,
|
|
154
|
+
var import_crypto, import_fs2, import_path2, QuotaExhaustedException, QuotaTracker, AnthropicProvider, OpenAIProvider, GeminiProvider, OllamaProvider, NullProvider;
|
|
112
155
|
var init_llm_client = __esm({
|
|
113
156
|
"packages/orchestrator/src/llm-client.ts"() {
|
|
114
157
|
"use strict";
|
|
115
158
|
import_crypto = require("crypto");
|
|
116
|
-
|
|
117
|
-
|
|
159
|
+
import_fs2 = require("fs");
|
|
160
|
+
import_path2 = require("path");
|
|
118
161
|
QuotaExhaustedException = class extends Error {
|
|
119
162
|
provider;
|
|
120
163
|
retryAfterMs;
|
|
@@ -128,7 +171,7 @@ var init_llm_client = __esm({
|
|
|
128
171
|
QuotaTracker = class {
|
|
129
172
|
constructor(provider, projectRoot) {
|
|
130
173
|
this.provider = provider;
|
|
131
|
-
this.statePath = projectRoot ? (0,
|
|
174
|
+
this.statePath = projectRoot ? (0, import_path2.join)(projectRoot, ".gossip", "quota-state.json") : null;
|
|
132
175
|
this.load();
|
|
133
176
|
}
|
|
134
177
|
consecutive429s = 0;
|
|
@@ -136,9 +179,9 @@ var init_llm_client = __esm({
|
|
|
136
179
|
reason = "quota";
|
|
137
180
|
statePath;
|
|
138
181
|
load() {
|
|
139
|
-
if (!this.statePath || !(0,
|
|
182
|
+
if (!this.statePath || !(0, import_fs2.existsSync)(this.statePath)) return;
|
|
140
183
|
try {
|
|
141
|
-
const state = JSON.parse((0,
|
|
184
|
+
const state = JSON.parse((0, import_fs2.readFileSync)(this.statePath, "utf-8"));
|
|
142
185
|
if (state[this.provider]) {
|
|
143
186
|
this.exhaustedUntil = state[this.provider].exhaustedUntil;
|
|
144
187
|
this.consecutive429s = state[this.provider].consecutive429s;
|
|
@@ -150,15 +193,15 @@ var init_llm_client = __esm({
|
|
|
150
193
|
persist() {
|
|
151
194
|
if (!this.statePath) return;
|
|
152
195
|
try {
|
|
153
|
-
const dir = (0,
|
|
154
|
-
if (!(0,
|
|
196
|
+
const dir = (0, import_path2.join)(this.statePath, "..");
|
|
197
|
+
if (!(0, import_fs2.existsSync)(dir)) (0, import_fs2.mkdirSync)(dir, { recursive: true });
|
|
155
198
|
let existing = {};
|
|
156
199
|
try {
|
|
157
|
-
existing = JSON.parse((0,
|
|
200
|
+
existing = JSON.parse((0, import_fs2.readFileSync)(this.statePath, "utf-8"));
|
|
158
201
|
} catch {
|
|
159
202
|
}
|
|
160
203
|
existing[this.provider] = { exhaustedUntil: this.exhaustedUntil, consecutive429s: this.consecutive429s, reason: this.reason };
|
|
161
|
-
(0,
|
|
204
|
+
(0, import_fs2.writeFileSync)(this.statePath, JSON.stringify(existing, null, 2));
|
|
162
205
|
} catch {
|
|
163
206
|
}
|
|
164
207
|
}
|
|
@@ -3078,7 +3121,7 @@ var init_gossip_agent = __esm({
|
|
|
3078
3121
|
}
|
|
3079
3122
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
3080
3123
|
connect() {
|
|
3081
|
-
return new Promise((
|
|
3124
|
+
return new Promise((resolve18, reject) => {
|
|
3082
3125
|
const ws = new import_ws.default(this.config.relayUrl);
|
|
3083
3126
|
const timeout = setTimeout(() => {
|
|
3084
3127
|
ws.removeAllListeners();
|
|
@@ -3111,7 +3154,7 @@ var init_gossip_agent = __esm({
|
|
|
3111
3154
|
ws.on("error", (err) => this.emit("error", err));
|
|
3112
3155
|
this.startKeepAlive();
|
|
3113
3156
|
this.emit("connect", msg.sessionId);
|
|
3114
|
-
|
|
3157
|
+
resolve18();
|
|
3115
3158
|
} else if (msg.type === "error") {
|
|
3116
3159
|
clearTimeout(timeout);
|
|
3117
3160
|
ws.removeAllListeners();
|
|
@@ -3137,7 +3180,7 @@ var init_gossip_agent = __esm({
|
|
|
3137
3180
|
this.reconnectTimer = null;
|
|
3138
3181
|
}
|
|
3139
3182
|
if (!this.ws) return;
|
|
3140
|
-
return new Promise((
|
|
3183
|
+
return new Promise((resolve18) => {
|
|
3141
3184
|
this.intentionalDisconnect = true;
|
|
3142
3185
|
this._connected = false;
|
|
3143
3186
|
const ws = this.ws;
|
|
@@ -3148,7 +3191,7 @@ var init_gossip_agent = __esm({
|
|
|
3148
3191
|
settled = true;
|
|
3149
3192
|
this.intentionalDisconnect = false;
|
|
3150
3193
|
this.emit("disconnect", code);
|
|
3151
|
-
|
|
3194
|
+
resolve18();
|
|
3152
3195
|
};
|
|
3153
3196
|
const timer = setTimeout(() => done(1e3), 2e3);
|
|
3154
3197
|
ws.once("close", (code) => {
|
|
@@ -3187,8 +3230,8 @@ var init_gossip_agent = __esm({
|
|
|
3187
3230
|
throw new Error("Not connected to relay");
|
|
3188
3231
|
}
|
|
3189
3232
|
const encoded = Buffer.from(this.codec.encode(envelope));
|
|
3190
|
-
return new Promise((
|
|
3191
|
-
this.ws.send(encoded, (err) => err ? reject(err) :
|
|
3233
|
+
return new Promise((resolve18, reject) => {
|
|
3234
|
+
this.ws.send(encoded, (err) => err ? reject(err) : resolve18());
|
|
3192
3235
|
});
|
|
3193
3236
|
}
|
|
3194
3237
|
// ─── Internal ────────────────────────────────────────────────────────────────
|
|
@@ -3632,7 +3675,7 @@ ${context}` : ""}
|
|
|
3632
3675
|
this.toolCallBudget.set(name, used + 1);
|
|
3633
3676
|
}
|
|
3634
3677
|
const requestId = (0, import_crypto4.randomUUID)();
|
|
3635
|
-
const resultPromise = new Promise((
|
|
3678
|
+
const resultPromise = new Promise((resolve18, reject) => {
|
|
3636
3679
|
const timer = setTimeout(() => {
|
|
3637
3680
|
if (this.pendingToolCalls.has(requestId)) {
|
|
3638
3681
|
this.pendingToolCalls.delete(requestId);
|
|
@@ -3643,7 +3686,7 @@ ${context}` : ""}
|
|
|
3643
3686
|
this.pendingToolCalls.set(requestId, {
|
|
3644
3687
|
resolve: (r) => {
|
|
3645
3688
|
clearTimeout(timer);
|
|
3646
|
-
|
|
3689
|
+
resolve18(r);
|
|
3647
3690
|
},
|
|
3648
3691
|
reject: (e) => {
|
|
3649
3692
|
clearTimeout(timer);
|
|
@@ -3708,12 +3751,12 @@ ${context}` : ""}
|
|
|
3708
3751
|
});
|
|
3709
3752
|
|
|
3710
3753
|
// packages/tools/src/file-tools.ts
|
|
3711
|
-
var import_promises,
|
|
3754
|
+
var import_promises, import_path3, FileTools;
|
|
3712
3755
|
var init_file_tools = __esm({
|
|
3713
3756
|
"packages/tools/src/file-tools.ts"() {
|
|
3714
3757
|
"use strict";
|
|
3715
3758
|
import_promises = require("fs/promises");
|
|
3716
|
-
|
|
3759
|
+
import_path3 = require("path");
|
|
3717
3760
|
FileTools = class {
|
|
3718
3761
|
constructor(sandbox) {
|
|
3719
3762
|
this.sandbox = sandbox;
|
|
@@ -3738,7 +3781,7 @@ var init_file_tools = __esm({
|
|
|
3738
3781
|
}
|
|
3739
3782
|
async fileWrite(args) {
|
|
3740
3783
|
const absPath = this.sandbox.validatePath(args.path);
|
|
3741
|
-
const dir = (0,
|
|
3784
|
+
const dir = (0, import_path3.resolve)(absPath, "..");
|
|
3742
3785
|
await (0, import_promises.mkdir)(dir, { recursive: true });
|
|
3743
3786
|
await (0, import_promises.writeFile)(absPath, args.content, "utf-8");
|
|
3744
3787
|
return `Written ${args.content.length} bytes to ${args.path}`;
|
|
@@ -3783,7 +3826,7 @@ var init_file_tools = __esm({
|
|
|
3783
3826
|
}
|
|
3784
3827
|
for (const entry of entries) {
|
|
3785
3828
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
3786
|
-
const fullPath = (0,
|
|
3829
|
+
const fullPath = (0, import_path3.join)(dir, entry);
|
|
3787
3830
|
let info;
|
|
3788
3831
|
try {
|
|
3789
3832
|
info = await (0, import_promises.stat)(fullPath);
|
|
@@ -3795,7 +3838,7 @@ var init_file_tools = __esm({
|
|
|
3795
3838
|
} else {
|
|
3796
3839
|
const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
3797
3840
|
const regex = new RegExp(regexStr);
|
|
3798
|
-
const relPath = (0,
|
|
3841
|
+
const relPath = (0, import_path3.relative)(this.sandbox.projectRoot, fullPath);
|
|
3799
3842
|
if (regex.test(entry) || regex.test(relPath)) {
|
|
3800
3843
|
results.push(relPath);
|
|
3801
3844
|
}
|
|
@@ -3812,7 +3855,7 @@ var init_file_tools = __esm({
|
|
|
3812
3855
|
}
|
|
3813
3856
|
for (const entry of entries) {
|
|
3814
3857
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
3815
|
-
const fullPath = (0,
|
|
3858
|
+
const fullPath = (0, import_path3.join)(dir, entry);
|
|
3816
3859
|
let info;
|
|
3817
3860
|
try {
|
|
3818
3861
|
info = await (0, import_promises.stat)(fullPath);
|
|
@@ -3825,7 +3868,7 @@ var init_file_tools = __esm({
|
|
|
3825
3868
|
try {
|
|
3826
3869
|
const content = await (0, import_promises.readFile)(fullPath, "utf-8");
|
|
3827
3870
|
const lines = content.split("\n");
|
|
3828
|
-
const relPath = (0,
|
|
3871
|
+
const relPath = (0, import_path3.relative)(this.sandbox.projectRoot, fullPath);
|
|
3829
3872
|
lines.forEach((line, idx) => {
|
|
3830
3873
|
if (regex.test(line)) {
|
|
3831
3874
|
results.push(`${relPath}:${idx + 1}: ${line}`);
|
|
@@ -3849,7 +3892,7 @@ var init_file_tools = __esm({
|
|
|
3849
3892
|
const entry = filtered[i];
|
|
3850
3893
|
const isLast = i === filtered.length - 1;
|
|
3851
3894
|
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
3852
|
-
const fullPath = (0,
|
|
3895
|
+
const fullPath = (0, import_path3.join)(dir, entry);
|
|
3853
3896
|
let info;
|
|
3854
3897
|
try {
|
|
3855
3898
|
info = await (0, import_promises.stat)(fullPath);
|
|
@@ -4054,20 +4097,20 @@ var init_git_tools = __esm({
|
|
|
4054
4097
|
});
|
|
4055
4098
|
|
|
4056
4099
|
// packages/tools/src/skill-tools.ts
|
|
4057
|
-
var
|
|
4100
|
+
var import_fs3, import_path4, SkillTools;
|
|
4058
4101
|
var init_skill_tools = __esm({
|
|
4059
4102
|
"packages/tools/src/skill-tools.ts"() {
|
|
4060
4103
|
"use strict";
|
|
4061
|
-
|
|
4062
|
-
|
|
4104
|
+
import_fs3 = require("fs");
|
|
4105
|
+
import_path4 = require("path");
|
|
4063
4106
|
SkillTools = class {
|
|
4064
4107
|
gapLogPath;
|
|
4065
4108
|
constructor(projectRoot) {
|
|
4066
|
-
const gossipDir2 = (0,
|
|
4067
|
-
if (!(0,
|
|
4068
|
-
(0,
|
|
4109
|
+
const gossipDir2 = (0, import_path4.join)(projectRoot, ".gossip");
|
|
4110
|
+
if (!(0, import_fs3.existsSync)(gossipDir2)) {
|
|
4111
|
+
(0, import_fs3.mkdirSync)(gossipDir2, { recursive: true });
|
|
4069
4112
|
}
|
|
4070
|
-
this.gapLogPath = (0,
|
|
4113
|
+
this.gapLogPath = (0, import_path4.join)(gossipDir2, "skill-gaps.jsonl");
|
|
4071
4114
|
}
|
|
4072
4115
|
async suggestSkill(args, callerId) {
|
|
4073
4116
|
const entry = {
|
|
@@ -4078,16 +4121,16 @@ var init_skill_tools = __esm({
|
|
|
4078
4121
|
task_context: args.task_context,
|
|
4079
4122
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4080
4123
|
};
|
|
4081
|
-
(0,
|
|
4124
|
+
(0, import_fs3.appendFileSync)(this.gapLogPath, JSON.stringify(entry) + "\n");
|
|
4082
4125
|
this.truncateIfNeeded();
|
|
4083
4126
|
return `Suggestion noted: '${args.skill_name}'. Continue with your current skills.`;
|
|
4084
4127
|
}
|
|
4085
4128
|
truncateIfNeeded() {
|
|
4086
4129
|
try {
|
|
4087
|
-
const content = (0,
|
|
4130
|
+
const content = (0, import_fs3.readFileSync)(this.gapLogPath, "utf-8");
|
|
4088
4131
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
4089
4132
|
if (lines.length > 5e3) {
|
|
4090
|
-
(0,
|
|
4133
|
+
(0, import_fs3.writeFileSync)(this.gapLogPath, lines.slice(-1e3).join("\n") + "\n");
|
|
4091
4134
|
}
|
|
4092
4135
|
} catch {
|
|
4093
4136
|
}
|
|
@@ -4097,16 +4140,16 @@ var init_skill_tools = __esm({
|
|
|
4097
4140
|
});
|
|
4098
4141
|
|
|
4099
4142
|
// packages/tools/src/sandbox.ts
|
|
4100
|
-
var
|
|
4143
|
+
var import_path5, import_fs4, Sandbox;
|
|
4101
4144
|
var init_sandbox = __esm({
|
|
4102
4145
|
"packages/tools/src/sandbox.ts"() {
|
|
4103
4146
|
"use strict";
|
|
4104
|
-
|
|
4105
|
-
|
|
4147
|
+
import_path5 = require("path");
|
|
4148
|
+
import_fs4 = require("fs");
|
|
4106
4149
|
Sandbox = class {
|
|
4107
4150
|
root;
|
|
4108
4151
|
constructor(projectRoot) {
|
|
4109
|
-
this.root = (0,
|
|
4152
|
+
this.root = (0, import_fs4.realpathSync)((0, import_path5.resolve)(projectRoot));
|
|
4110
4153
|
}
|
|
4111
4154
|
get projectRoot() {
|
|
4112
4155
|
return this.root;
|
|
@@ -4118,14 +4161,14 @@ var init_sandbox = __esm({
|
|
|
4118
4161
|
* Resolves symlinks to prevent symlink escape attacks.
|
|
4119
4162
|
*/
|
|
4120
4163
|
validatePath(filePath) {
|
|
4121
|
-
const resolved = (0,
|
|
4164
|
+
const resolved = (0, import_path5.resolve)(this.root, filePath);
|
|
4122
4165
|
let checkPath = resolved;
|
|
4123
|
-
while (!(0,
|
|
4124
|
-
const parent = (0,
|
|
4166
|
+
while (!(0, import_fs4.existsSync)(checkPath)) {
|
|
4167
|
+
const parent = (0, import_path5.dirname)(checkPath);
|
|
4125
4168
|
if (parent === checkPath) break;
|
|
4126
4169
|
checkPath = parent;
|
|
4127
4170
|
}
|
|
4128
|
-
const real = (0,
|
|
4171
|
+
const real = (0, import_fs4.existsSync)(checkPath) ? (0, import_fs4.realpathSync)(checkPath) : checkPath;
|
|
4129
4172
|
const remainder = resolved.slice(checkPath.length);
|
|
4130
4173
|
const fullReal = real + remainder;
|
|
4131
4174
|
if (!fullReal.startsWith(this.root + "/") && fullReal !== this.root) {
|
|
@@ -8343,16 +8386,16 @@ var init_tool_schemas = __esm({
|
|
|
8343
8386
|
// packages/tools/src/tool-server.ts
|
|
8344
8387
|
function canonicalizeForBoundary(p) {
|
|
8345
8388
|
let out = p;
|
|
8346
|
-
if ((0,
|
|
8389
|
+
if ((0, import_fs5.existsSync)(out)) {
|
|
8347
8390
|
try {
|
|
8348
|
-
out = (0,
|
|
8391
|
+
out = (0, import_fs5.realpathSync)(out);
|
|
8349
8392
|
} catch {
|
|
8350
8393
|
}
|
|
8351
8394
|
} else {
|
|
8352
|
-
const parent = (0,
|
|
8353
|
-
if (parent !== out && (0,
|
|
8395
|
+
const parent = (0, import_path6.dirname)(out);
|
|
8396
|
+
if (parent !== out && (0, import_fs5.existsSync)(parent)) {
|
|
8354
8397
|
try {
|
|
8355
|
-
out = (0,
|
|
8398
|
+
out = (0, import_path6.join)((0, import_fs5.realpathSync)(parent), (0, import_path6.basename)(out));
|
|
8356
8399
|
} catch {
|
|
8357
8400
|
}
|
|
8358
8401
|
}
|
|
@@ -8385,7 +8428,7 @@ function truncateAtLine(text, maxLength) {
|
|
|
8385
8428
|
const cut = text.lastIndexOf("\n", maxLength);
|
|
8386
8429
|
return text.slice(0, cut !== -1 ? cut : maxLength);
|
|
8387
8430
|
}
|
|
8388
|
-
var import_msgpack4, import_crypto5,
|
|
8431
|
+
var import_msgpack4, import_crypto5, import_path6, import_fs5, CASE_INSENSITIVE_FS, ToolServer;
|
|
8389
8432
|
var init_tool_server = __esm({
|
|
8390
8433
|
"packages/tools/src/tool-server.ts"() {
|
|
8391
8434
|
"use strict";
|
|
@@ -8399,8 +8442,8 @@ var init_tool_server = __esm({
|
|
|
8399
8442
|
init_skill_tools();
|
|
8400
8443
|
init_sandbox();
|
|
8401
8444
|
init_tool_schemas();
|
|
8402
|
-
|
|
8403
|
-
|
|
8445
|
+
import_path6 = require("path");
|
|
8446
|
+
import_fs5 = require("fs");
|
|
8404
8447
|
CASE_INSENSITIVE_FS = process.platform === "darwin" || process.platform === "win32";
|
|
8405
8448
|
ToolServer = class _ToolServer {
|
|
8406
8449
|
agent;
|
|
@@ -8455,12 +8498,12 @@ var init_tool_server = __esm({
|
|
|
8455
8498
|
return this.agent.agentId;
|
|
8456
8499
|
}
|
|
8457
8500
|
assignScope(agentId, scope) {
|
|
8458
|
-
const abs = (0,
|
|
8501
|
+
const abs = (0, import_path6.resolve)(this.sandbox.projectRoot, scope);
|
|
8459
8502
|
this.agentScopes.set(agentId, canonicalizeForBoundary(abs));
|
|
8460
8503
|
this.writeAgents.add(agentId);
|
|
8461
8504
|
}
|
|
8462
8505
|
assignRoot(agentId, root) {
|
|
8463
|
-
const abs = (0,
|
|
8506
|
+
const abs = (0, import_path6.resolve)(root);
|
|
8464
8507
|
this.agentRoots.set(agentId, canonicalizeForBoundary(abs));
|
|
8465
8508
|
this.writeAgents.add(agentId);
|
|
8466
8509
|
}
|
|
@@ -8520,13 +8563,13 @@ var init_tool_server = __esm({
|
|
|
8520
8563
|
if (toolName === "file_write") {
|
|
8521
8564
|
const filePath = args.path;
|
|
8522
8565
|
if (scope) {
|
|
8523
|
-
const canonical = canonicalizeForBoundary((0,
|
|
8566
|
+
const canonical = canonicalizeForBoundary((0, import_path6.resolve)(this.sandbox.projectRoot, filePath));
|
|
8524
8567
|
if (!canonical.startsWith(scope)) {
|
|
8525
8568
|
throw new Error(`Write blocked: "${filePath}" is outside scope "${scope}"`);
|
|
8526
8569
|
}
|
|
8527
8570
|
}
|
|
8528
8571
|
if (root) {
|
|
8529
|
-
const canonical = canonicalizeForBoundary((0,
|
|
8572
|
+
const canonical = canonicalizeForBoundary((0, import_path6.resolve)(root, filePath));
|
|
8530
8573
|
if (!canonical.startsWith(root)) {
|
|
8531
8574
|
throw new Error(`Write blocked: "${filePath}" is outside worktree root "${root}"`);
|
|
8532
8575
|
}
|
|
@@ -8561,13 +8604,13 @@ var init_tool_server = __esm({
|
|
|
8561
8604
|
if (toolName === "file_delete") {
|
|
8562
8605
|
const filePath = args.path;
|
|
8563
8606
|
if (scope) {
|
|
8564
|
-
const canonical = canonicalizeForBoundary((0,
|
|
8607
|
+
const canonical = canonicalizeForBoundary((0, import_path6.resolve)(this.sandbox.projectRoot, filePath));
|
|
8565
8608
|
if (!canonical.startsWith(scope)) {
|
|
8566
8609
|
throw new Error(`Delete blocked: "${filePath}" is outside scope "${scope}"`);
|
|
8567
8610
|
}
|
|
8568
8611
|
}
|
|
8569
8612
|
if (root) {
|
|
8570
|
-
const canonical = canonicalizeForBoundary((0,
|
|
8613
|
+
const canonical = canonicalizeForBoundary((0, import_path6.resolve)(root, filePath));
|
|
8571
8614
|
if (!canonical.startsWith(root)) {
|
|
8572
8615
|
throw new Error(`Delete blocked: "${filePath}" is outside worktree root "${root}"`);
|
|
8573
8616
|
}
|
|
@@ -8603,7 +8646,7 @@ var init_tool_server = __esm({
|
|
|
8603
8646
|
case "file_read": {
|
|
8604
8647
|
const readScope = callerId ? this.agentScopes.get(callerId) : void 0;
|
|
8605
8648
|
if (readScope) {
|
|
8606
|
-
const canonical = canonicalizeForBoundary((0,
|
|
8649
|
+
const canonical = canonicalizeForBoundary((0, import_path6.resolve)(this.sandbox.projectRoot, args.path));
|
|
8607
8650
|
if (!canonical.startsWith(readScope)) {
|
|
8608
8651
|
throw new Error(`Read blocked: "${args.path}" is outside scope "${readScope}"`);
|
|
8609
8652
|
}
|
|
@@ -8797,7 +8840,7 @@ ${truncateAtLine(fullDiff, 3e3)}`;
|
|
|
8797
8840
|
}
|
|
8798
8841
|
async requestPeerReview(callerId, diff, testResult) {
|
|
8799
8842
|
const requestId = (0, import_crypto5.randomUUID)();
|
|
8800
|
-
const reviewPromise = new Promise((
|
|
8843
|
+
const reviewPromise = new Promise((resolve18, reject) => {
|
|
8801
8844
|
const timer = setTimeout(() => {
|
|
8802
8845
|
this.pendingReviews.delete(requestId);
|
|
8803
8846
|
reject(new Error("Review timed out"));
|
|
@@ -8806,7 +8849,7 @@ ${truncateAtLine(fullDiff, 3e3)}`;
|
|
|
8806
8849
|
this.pendingReviews.set(requestId, {
|
|
8807
8850
|
resolve: (r) => {
|
|
8808
8851
|
clearTimeout(timer);
|
|
8809
|
-
|
|
8852
|
+
resolve18(r);
|
|
8810
8853
|
},
|
|
8811
8854
|
reject: (e) => {
|
|
8812
8855
|
clearTimeout(timer);
|
|
@@ -8832,7 +8875,7 @@ ${truncateAtLine(fullDiff, 3e3)}`;
|
|
|
8832
8875
|
}
|
|
8833
8876
|
async handleRunTests(args, callerId) {
|
|
8834
8877
|
const { fileGlob } = args;
|
|
8835
|
-
const resolvedGlob = (0,
|
|
8878
|
+
const resolvedGlob = (0, import_path6.resolve)(this.sandbox.projectRoot, fileGlob.replace(/\*/g, "_"));
|
|
8836
8879
|
if (!resolvedGlob.startsWith(this.sandbox.projectRoot)) {
|
|
8837
8880
|
throw new Error("run_tests: fileGlob must not contain path traversal");
|
|
8838
8881
|
}
|
|
@@ -8840,7 +8883,7 @@ ${truncateAtLine(fullDiff, 3e3)}`;
|
|
|
8840
8883
|
throw new Error("run_tests: fileGlob must not contain flags. Pass only file paths/globs.");
|
|
8841
8884
|
}
|
|
8842
8885
|
const scope = callerId ? this.agentScopes.get(callerId) : void 0;
|
|
8843
|
-
const cwd = scope ? this.sandbox.validatePath((0,
|
|
8886
|
+
const cwd = scope ? this.sandbox.validatePath((0, import_path6.resolve)(this.sandbox.projectRoot, scope)) : this.sandbox.projectRoot;
|
|
8844
8887
|
let output;
|
|
8845
8888
|
let success2;
|
|
8846
8889
|
try {
|
|
@@ -8860,11 +8903,11 @@ ${truncateAtLine(fullDiff, 3e3)}`;
|
|
|
8860
8903
|
}
|
|
8861
8904
|
async handleRunTypecheck(callerId) {
|
|
8862
8905
|
const scope = callerId ? this.agentScopes.get(callerId) : void 0;
|
|
8863
|
-
const cwd = scope ? this.sandbox.validatePath((0,
|
|
8906
|
+
const cwd = scope ? this.sandbox.validatePath((0, import_path6.resolve)(this.sandbox.projectRoot, scope)) : this.sandbox.projectRoot;
|
|
8864
8907
|
let output;
|
|
8865
8908
|
let success2;
|
|
8866
8909
|
try {
|
|
8867
|
-
const tsconfigPath = (0,
|
|
8910
|
+
const tsconfigPath = (0, import_path6.resolve)(this.sandbox.projectRoot, "tsconfig.json");
|
|
8868
8911
|
output = await this.shellTools.shellExec({
|
|
8869
8912
|
command: "npx",
|
|
8870
8913
|
args: ["tsc", "--noEmit", "--project", tsconfigPath],
|
|
@@ -9179,6 +9222,17 @@ function loadSkills(agentId, skills, projectRoot, index, task) {
|
|
|
9179
9222
|
for (const skill of effectiveSkills) {
|
|
9180
9223
|
const content = resolveSkill(agentId, skill, projectRoot);
|
|
9181
9224
|
if (!content) continue;
|
|
9225
|
+
const frontmatterStatus = parseSkillFrontmatter(content)?.status;
|
|
9226
|
+
if (frontmatterStatus === "failed" || frontmatterStatus === "silent_skill") {
|
|
9227
|
+
process.stderr.write(`[gossipcat] Skipping ${frontmatterStatus} skill ${agentId}/${skill} from injection
|
|
9228
|
+
`);
|
|
9229
|
+
dropped.push(skill);
|
|
9230
|
+
continue;
|
|
9231
|
+
}
|
|
9232
|
+
if (frontmatterStatus === "flagged_for_manual_review") {
|
|
9233
|
+
process.stderr.write(`[gossipcat] Injecting flagged_for_manual_review skill ${agentId}/${skill} \u2014 manual review recommended
|
|
9234
|
+
`);
|
|
9235
|
+
}
|
|
9182
9236
|
const mode = index?.getSkillMode(agentId, skill) ?? "permanent";
|
|
9183
9237
|
if (mode === "permanent") {
|
|
9184
9238
|
permanent.push({ name: skill, content });
|
|
@@ -9247,16 +9301,16 @@ function resolveSkill(agentId, skill, projectRoot) {
|
|
|
9247
9301
|
if (!normalized) return null;
|
|
9248
9302
|
const filename = `${normalized}.md`;
|
|
9249
9303
|
const bases = [
|
|
9250
|
-
(0,
|
|
9251
|
-
(0,
|
|
9252
|
-
(0,
|
|
9304
|
+
(0, import_path7.resolve)(projectRoot, ".gossip", "agents", agentId, "skills"),
|
|
9305
|
+
(0, import_path7.resolve)(projectRoot, ".gossip", "skills"),
|
|
9306
|
+
(0, import_path7.resolve)(__dirname, "default-skills")
|
|
9253
9307
|
];
|
|
9254
9308
|
for (const base of bases) {
|
|
9255
|
-
const candidate = (0,
|
|
9256
|
-
if (!candidate.startsWith(base +
|
|
9257
|
-
if ((0,
|
|
9309
|
+
const candidate = (0, import_path7.resolve)(base, filename);
|
|
9310
|
+
if (!candidate.startsWith(base + import_path7.sep)) continue;
|
|
9311
|
+
if ((0, import_fs6.existsSync)(candidate)) {
|
|
9258
9312
|
try {
|
|
9259
|
-
return (0,
|
|
9313
|
+
return (0, import_fs6.readFileSync)(candidate, "utf-8");
|
|
9260
9314
|
} catch (err) {
|
|
9261
9315
|
process.stderr.write(
|
|
9262
9316
|
`[skill-loader] Failed to read skill file ${candidate}: ${err?.message ?? err}
|
|
@@ -9271,12 +9325,12 @@ function resolveSkill(agentId, skill, projectRoot) {
|
|
|
9271
9325
|
function resolveSkillExists(agentId, skill, projectRoot) {
|
|
9272
9326
|
return resolveSkill(agentId, skill, projectRoot) !== null;
|
|
9273
9327
|
}
|
|
9274
|
-
var
|
|
9328
|
+
var import_fs6, import_path7, SAFE_AGENT_ID, MAX_CONTEXTUAL_SKILLS, MIN_KEYWORD_HITS, DEFAULT_KEYWORDS, patternCache, MAX_PATTERN_CACHE, MAX_KEYWORD_LENGTH;
|
|
9275
9329
|
var init_skill_loader = __esm({
|
|
9276
9330
|
"packages/orchestrator/src/skill-loader.ts"() {
|
|
9277
9331
|
"use strict";
|
|
9278
|
-
|
|
9279
|
-
|
|
9332
|
+
import_fs6 = require("fs");
|
|
9333
|
+
import_path7 = require("path");
|
|
9280
9334
|
init_skill_parser();
|
|
9281
9335
|
init_skill_name();
|
|
9282
9336
|
SAFE_AGENT_ID = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
@@ -9475,29 +9529,29 @@ Attributes can appear in any order. Do NOT include confirmations.`;
|
|
|
9475
9529
|
});
|
|
9476
9530
|
|
|
9477
9531
|
// packages/orchestrator/src/agent-memory.ts
|
|
9478
|
-
var
|
|
9532
|
+
var import_fs7, import_path8, AgentMemoryReader;
|
|
9479
9533
|
var init_agent_memory = __esm({
|
|
9480
9534
|
"packages/orchestrator/src/agent-memory.ts"() {
|
|
9481
9535
|
"use strict";
|
|
9482
|
-
|
|
9483
|
-
|
|
9536
|
+
import_fs7 = require("fs");
|
|
9537
|
+
import_path8 = require("path");
|
|
9484
9538
|
AgentMemoryReader = class {
|
|
9485
9539
|
constructor(projectRoot) {
|
|
9486
9540
|
this.projectRoot = projectRoot;
|
|
9487
9541
|
}
|
|
9488
9542
|
loadMemory(agentId, taskText) {
|
|
9489
|
-
const memDir = (0,
|
|
9490
|
-
const indexPath = (0,
|
|
9491
|
-
if (!(0,
|
|
9543
|
+
const memDir = (0, import_path8.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
|
|
9544
|
+
const indexPath = (0, import_path8.join)(memDir, "MEMORY.md");
|
|
9545
|
+
if (!(0, import_fs7.existsSync)(indexPath)) return null;
|
|
9492
9546
|
const parts = [];
|
|
9493
|
-
const indexContent = (0,
|
|
9547
|
+
const indexContent = (0, import_fs7.readFileSync)(indexPath, "utf-8");
|
|
9494
9548
|
const indexLines = indexContent.split("\n");
|
|
9495
9549
|
parts.push(indexLines.length > 200 ? indexLines.slice(0, 200).join("\n") + "\n[Truncated]" : indexContent);
|
|
9496
|
-
const knowledgeDir = (0,
|
|
9497
|
-
if ((0,
|
|
9550
|
+
const knowledgeDir = (0, import_path8.join)(memDir, "knowledge");
|
|
9551
|
+
if ((0, import_fs7.existsSync)(knowledgeDir)) {
|
|
9498
9552
|
const files = this.selectKnowledgeFiles(knowledgeDir, taskText);
|
|
9499
9553
|
for (const file2 of files) {
|
|
9500
|
-
let content = (0,
|
|
9554
|
+
let content = (0, import_fs7.readFileSync)(file2.path, "utf-8");
|
|
9501
9555
|
content = content.replace(/<\/?(?:agent-memory|system|instructions)>/gi, "");
|
|
9502
9556
|
parts.push(`<agent-memory>
|
|
9503
9557
|
${content}
|
|
@@ -9507,11 +9561,11 @@ ${content}
|
|
|
9507
9561
|
}
|
|
9508
9562
|
}
|
|
9509
9563
|
}
|
|
9510
|
-
const projectKnowledgeDir = (0,
|
|
9511
|
-
if ((0,
|
|
9564
|
+
const projectKnowledgeDir = (0, import_path8.join)(this.projectRoot, ".gossip", "agents", "_project", "memory", "knowledge");
|
|
9565
|
+
if ((0, import_fs7.existsSync)(projectKnowledgeDir)) {
|
|
9512
9566
|
const projectFiles = this.selectKnowledgeFiles(projectKnowledgeDir, taskText, 3);
|
|
9513
9567
|
for (const file2 of projectFiles) {
|
|
9514
|
-
let content = (0,
|
|
9568
|
+
let content = (0, import_fs7.readFileSync)(file2.path, "utf-8");
|
|
9515
9569
|
content = content.replace(/<\/?(?:agent-memory|system|instructions)>/gi, "");
|
|
9516
9570
|
parts.push(`<project-context>
|
|
9517
9571
|
${content}
|
|
@@ -9521,19 +9575,19 @@ ${content}
|
|
|
9521
9575
|
}
|
|
9522
9576
|
}
|
|
9523
9577
|
}
|
|
9524
|
-
const calPath = (0,
|
|
9525
|
-
if ((0,
|
|
9526
|
-
parts.push((0,
|
|
9578
|
+
const calPath = (0, import_path8.join)(memDir, "calibration", "accuracy.md");
|
|
9579
|
+
if ((0, import_fs7.existsSync)(calPath)) {
|
|
9580
|
+
parts.push((0, import_fs7.readFileSync)(calPath, "utf-8"));
|
|
9527
9581
|
}
|
|
9528
9582
|
return parts.join("\n\n");
|
|
9529
9583
|
}
|
|
9530
9584
|
selectKnowledgeFiles(knowledgeDir, taskText, maxFiles = 5) {
|
|
9531
|
-
const files = (0,
|
|
9585
|
+
const files = (0, import_fs7.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
|
|
9532
9586
|
const scored = [];
|
|
9533
9587
|
const lower = taskText.toLowerCase();
|
|
9534
9588
|
for (const file2 of files) {
|
|
9535
|
-
const filePath = (0,
|
|
9536
|
-
const content = (0,
|
|
9589
|
+
const filePath = (0, import_path8.join)(knowledgeDir, file2);
|
|
9590
|
+
const content = (0, import_fs7.readFileSync)(filePath, "utf-8");
|
|
9537
9591
|
const frontmatter = this.parseFrontmatter(content);
|
|
9538
9592
|
if (frontmatter) {
|
|
9539
9593
|
const warmth = this.calculateWarmth(frontmatter.importance, frontmatter.lastAccessed);
|
|
@@ -9547,7 +9601,7 @@ ${content}
|
|
|
9547
9601
|
} else {
|
|
9548
9602
|
const relevance = this.calculateRelevance(content.slice(0, 500), lower);
|
|
9549
9603
|
try {
|
|
9550
|
-
const mtime = (0,
|
|
9604
|
+
const mtime = (0, import_fs7.statSync)(filePath).mtimeMs;
|
|
9551
9605
|
const ageDays = (Date.now() - mtime) / 864e5;
|
|
9552
9606
|
const ageFactor = 1 / (1 + ageDays / 30);
|
|
9553
9607
|
scored.push({ path: filePath, score: Math.max(relevance * ageFactor, 0.05) });
|
|
@@ -9602,7 +9656,7 @@ ${content}
|
|
|
9602
9656
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9603
9657
|
const fmEnd = content.indexOf("\n---", 4);
|
|
9604
9658
|
if (fmEnd < 0) {
|
|
9605
|
-
(0,
|
|
9659
|
+
(0, import_fs7.writeFileSync)(filePath, content);
|
|
9606
9660
|
return;
|
|
9607
9661
|
}
|
|
9608
9662
|
let frontmatter = content.slice(0, fmEnd);
|
|
@@ -9613,19 +9667,19 @@ ${content}
|
|
|
9613
9667
|
const newCount = parseInt(countMatch[1]) + 1;
|
|
9614
9668
|
frontmatter = frontmatter.replace(/accessCount:\s*\d+/, `accessCount: ${newCount}`);
|
|
9615
9669
|
}
|
|
9616
|
-
(0,
|
|
9670
|
+
(0, import_fs7.writeFileSync)(filePath, frontmatter + body);
|
|
9617
9671
|
}
|
|
9618
9672
|
};
|
|
9619
9673
|
}
|
|
9620
9674
|
});
|
|
9621
9675
|
|
|
9622
9676
|
// packages/orchestrator/src/memory-compactor.ts
|
|
9623
|
-
var
|
|
9677
|
+
var import_fs8, import_path9, MAX_ARCHIVE_LINES, MemoryCompactor;
|
|
9624
9678
|
var init_memory_compactor = __esm({
|
|
9625
9679
|
"packages/orchestrator/src/memory-compactor.ts"() {
|
|
9626
9680
|
"use strict";
|
|
9627
|
-
|
|
9628
|
-
|
|
9681
|
+
import_fs8 = require("fs");
|
|
9682
|
+
import_path9 = require("path");
|
|
9629
9683
|
MAX_ARCHIVE_LINES = 5e3;
|
|
9630
9684
|
MemoryCompactor = class {
|
|
9631
9685
|
constructor(projectRoot) {
|
|
@@ -9637,32 +9691,32 @@ var init_memory_compactor = __esm({
|
|
|
9637
9691
|
return (importance ?? 0.5) * (1 / (1 + days / 30));
|
|
9638
9692
|
}
|
|
9639
9693
|
compactIfNeeded(agentId, maxEntries = 20) {
|
|
9640
|
-
const memDir = (0,
|
|
9641
|
-
const tasksPath = (0,
|
|
9642
|
-
const lockPath = (0,
|
|
9643
|
-
if (!(0,
|
|
9644
|
-
if ((0,
|
|
9694
|
+
const memDir = (0, import_path9.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
|
|
9695
|
+
const tasksPath = (0, import_path9.join)(memDir, "tasks.jsonl");
|
|
9696
|
+
const lockPath = (0, import_path9.join)(memDir, "tasks.jsonl.lock");
|
|
9697
|
+
if (!(0, import_fs8.existsSync)(tasksPath)) return { archived: 0 };
|
|
9698
|
+
if ((0, import_fs8.existsSync)(lockPath)) {
|
|
9645
9699
|
try {
|
|
9646
|
-
const lockTs = parseInt((0,
|
|
9700
|
+
const lockTs = parseInt((0, import_fs8.readFileSync)(lockPath, "utf-8"), 10);
|
|
9647
9701
|
if (!Number.isNaN(lockTs) && Date.now() - lockTs < 6e4) return { archived: 0 };
|
|
9648
|
-
(0,
|
|
9702
|
+
(0, import_fs8.unlinkSync)(lockPath);
|
|
9649
9703
|
} catch {
|
|
9650
9704
|
try {
|
|
9651
|
-
(0,
|
|
9705
|
+
(0, import_fs8.unlinkSync)(lockPath);
|
|
9652
9706
|
} catch {
|
|
9653
9707
|
return { archived: 0 };
|
|
9654
9708
|
}
|
|
9655
9709
|
}
|
|
9656
9710
|
}
|
|
9657
9711
|
try {
|
|
9658
|
-
const fd = (0,
|
|
9659
|
-
(0,
|
|
9660
|
-
(0,
|
|
9712
|
+
const fd = (0, import_fs8.openSync)(lockPath, import_fs8.constants.O_WRONLY | import_fs8.constants.O_CREAT | import_fs8.constants.O_EXCL);
|
|
9713
|
+
(0, import_fs8.writeFileSync)(fd, `${Date.now()}`);
|
|
9714
|
+
(0, import_fs8.closeSync)(fd);
|
|
9661
9715
|
} catch {
|
|
9662
9716
|
return { archived: 0 };
|
|
9663
9717
|
}
|
|
9664
9718
|
try {
|
|
9665
|
-
const lines = (0,
|
|
9719
|
+
const lines = (0, import_fs8.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
9666
9720
|
if (lines.length <= maxEntries) return { archived: 0 };
|
|
9667
9721
|
const entries = [];
|
|
9668
9722
|
let dropped = 0;
|
|
@@ -9679,7 +9733,7 @@ var init_memory_compactor = __esm({
|
|
|
9679
9733
|
const toArchive = entries.slice(0, entries.length - maxEntries);
|
|
9680
9734
|
const toKeep = entries.slice(entries.length - maxEntries);
|
|
9681
9735
|
toKeep.sort((a, b) => a.idx - b.idx);
|
|
9682
|
-
const archivePath = (0,
|
|
9736
|
+
const archivePath = (0, import_path9.join)(memDir, "archive.jsonl");
|
|
9683
9737
|
for (const item of toArchive) {
|
|
9684
9738
|
const archived = {
|
|
9685
9739
|
archivedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -9687,22 +9741,22 @@ var init_memory_compactor = __esm({
|
|
|
9687
9741
|
warmth: item.warmth,
|
|
9688
9742
|
entry: item.entry
|
|
9689
9743
|
};
|
|
9690
|
-
(0,
|
|
9744
|
+
(0, import_fs8.appendFileSync)(archivePath, JSON.stringify(archived) + "\n");
|
|
9691
9745
|
}
|
|
9692
9746
|
try {
|
|
9693
|
-
if ((0,
|
|
9694
|
-
const archiveLines = (0,
|
|
9747
|
+
if ((0, import_fs8.existsSync)(archivePath)) {
|
|
9748
|
+
const archiveLines = (0, import_fs8.readFileSync)(archivePath, "utf-8").trim().split("\n");
|
|
9695
9749
|
if (archiveLines.length > MAX_ARCHIVE_LINES) {
|
|
9696
|
-
(0,
|
|
9750
|
+
(0, import_fs8.writeFileSync)(archivePath, archiveLines.slice(-MAX_ARCHIVE_LINES).join("\n") + "\n");
|
|
9697
9751
|
}
|
|
9698
9752
|
}
|
|
9699
9753
|
} catch {
|
|
9700
9754
|
}
|
|
9701
|
-
(0,
|
|
9755
|
+
(0, import_fs8.writeFileSync)(tasksPath, toKeep.map((e) => e.line).join("\n") + "\n");
|
|
9702
9756
|
return { archived: toArchive.length, dropped: dropped || void 0, message: `Compacted ${toArchive.length} memories for ${agentId}${dropped ? ` (${dropped} malformed lines dropped)` : ""}` };
|
|
9703
9757
|
} finally {
|
|
9704
9758
|
try {
|
|
9705
|
-
(0,
|
|
9759
|
+
(0, import_fs8.unlinkSync)(lockPath);
|
|
9706
9760
|
} catch {
|
|
9707
9761
|
}
|
|
9708
9762
|
}
|
|
@@ -9721,11 +9775,11 @@ function sanitizeName(name) {
|
|
|
9721
9775
|
function discoverProjectStructure(projectRoot) {
|
|
9722
9776
|
try {
|
|
9723
9777
|
const parts = [];
|
|
9724
|
-
const entries = (0,
|
|
9778
|
+
const entries = (0, import_fs9.readdirSync)(projectRoot, { withFileTypes: true });
|
|
9725
9779
|
for (const entry of entries) {
|
|
9726
9780
|
if (!entry.isDirectory() || isSkipped(entry.name)) continue;
|
|
9727
9781
|
try {
|
|
9728
|
-
const allChildren = (0,
|
|
9782
|
+
const allChildren = (0, import_fs9.readdirSync)((0, import_path10.join)(projectRoot, entry.name)).filter((f) => !f.startsWith("."));
|
|
9729
9783
|
if (allChildren.length === 0) continue;
|
|
9730
9784
|
const shown = allChildren.slice(0, MAX_CHILDREN).map(sanitizeName);
|
|
9731
9785
|
const suffix = allChildren.length > MAX_CHILDREN ? `, ...${allChildren.length - MAX_CHILDREN} more` : "";
|
|
@@ -9738,12 +9792,12 @@ function discoverProjectStructure(projectRoot) {
|
|
|
9738
9792
|
return [];
|
|
9739
9793
|
}
|
|
9740
9794
|
}
|
|
9741
|
-
var
|
|
9795
|
+
var import_fs9, import_path10, SKIP, MAX_CHILDREN;
|
|
9742
9796
|
var init_project_structure = __esm({
|
|
9743
9797
|
"packages/orchestrator/src/project-structure.ts"() {
|
|
9744
9798
|
"use strict";
|
|
9745
|
-
|
|
9746
|
-
|
|
9799
|
+
import_fs9 = require("fs");
|
|
9800
|
+
import_path10 = require("path");
|
|
9747
9801
|
SKIP = /* @__PURE__ */ new Set([
|
|
9748
9802
|
"node_modules",
|
|
9749
9803
|
"build",
|
|
@@ -9784,12 +9838,12 @@ function truncateStartAndEnd(text, maxLen) {
|
|
|
9784
9838
|
|
|
9785
9839
|
${text.slice(-tail)}`;
|
|
9786
9840
|
}
|
|
9787
|
-
var
|
|
9841
|
+
var import_fs10, import_path11, MemoryWriter;
|
|
9788
9842
|
var init_memory_writer = __esm({
|
|
9789
9843
|
"packages/orchestrator/src/memory-writer.ts"() {
|
|
9790
9844
|
"use strict";
|
|
9791
|
-
|
|
9792
|
-
|
|
9845
|
+
import_fs10 = require("fs");
|
|
9846
|
+
import_path11 = require("path");
|
|
9793
9847
|
init_memory_compactor();
|
|
9794
9848
|
init_project_structure();
|
|
9795
9849
|
MemoryWriter = class {
|
|
@@ -9802,12 +9856,12 @@ var init_memory_writer = __esm({
|
|
|
9802
9856
|
this.summaryLlm = llm;
|
|
9803
9857
|
}
|
|
9804
9858
|
getMemDir(agentId) {
|
|
9805
|
-
return (0,
|
|
9859
|
+
return (0, import_path11.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
|
|
9806
9860
|
}
|
|
9807
9861
|
ensureDirs(agentId) {
|
|
9808
9862
|
const memDir = this.getMemDir(agentId);
|
|
9809
|
-
(0,
|
|
9810
|
-
(0,
|
|
9863
|
+
(0, import_fs10.mkdirSync)((0, import_path11.join)(memDir, "knowledge"), { recursive: true });
|
|
9864
|
+
(0, import_fs10.mkdirSync)((0, import_path11.join)(memDir, "calibration"), { recursive: true });
|
|
9811
9865
|
return memDir;
|
|
9812
9866
|
}
|
|
9813
9867
|
async writeTaskEntry(agentId, data) {
|
|
@@ -9825,7 +9879,7 @@ var init_memory_writer = __esm({
|
|
|
9825
9879
|
importance: this.deriveImportance(data.scores),
|
|
9826
9880
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9827
9881
|
};
|
|
9828
|
-
(0,
|
|
9882
|
+
(0, import_fs10.appendFileSync)((0, import_path11.join)(memDir, "tasks.jsonl"), JSON.stringify(entry) + "\n");
|
|
9829
9883
|
}
|
|
9830
9884
|
/**
|
|
9831
9885
|
* Extract key facts from a task result and write as a knowledge entry.
|
|
@@ -9834,7 +9888,7 @@ var init_memory_writer = __esm({
|
|
|
9834
9888
|
*/
|
|
9835
9889
|
async writeKnowledgeFromResult(agentId, data) {
|
|
9836
9890
|
const memDir = this.ensureDirs(agentId);
|
|
9837
|
-
const knowledgeDir = (0,
|
|
9891
|
+
const knowledgeDir = (0, import_path11.join)(memDir, "knowledge");
|
|
9838
9892
|
const safeResult = data.result.length > 5e4 ? data.result.slice(0, 5e4) : data.result;
|
|
9839
9893
|
let cognitiveSummary = null;
|
|
9840
9894
|
if (this.summaryLlm) {
|
|
@@ -9884,7 +9938,7 @@ ${cleanSummary}` : cleanSummary;
|
|
|
9884
9938
|
...data.agentAccuracy !== void 0 && data.agentAccuracy < 0.4 ? ["> \u26A0 This agent has low accuracy (" + (data.agentAccuracy * 100).toFixed(0) + "%). Treat factual claims as unverified.\n"] : [],
|
|
9885
9939
|
body
|
|
9886
9940
|
].join("\n");
|
|
9887
|
-
(0,
|
|
9941
|
+
(0, import_fs10.writeFileSync)((0, import_path11.join)(knowledgeDir, filename), content);
|
|
9888
9942
|
}
|
|
9889
9943
|
/**
|
|
9890
9944
|
* Generate a cognitive summary — what the agent learned, not just what it saw.
|
|
@@ -9925,7 +9979,7 @@ ${truncateStartAndEnd(result, 4e3)}`
|
|
|
9925
9979
|
/** Session summary data shared across public methods */
|
|
9926
9980
|
sessionSummaryData(data) {
|
|
9927
9981
|
const memDir = this.ensureDirs("_project");
|
|
9928
|
-
const knowledgeDir = (0,
|
|
9982
|
+
const knowledgeDir = (0, import_path11.join)(memDir, "knowledge");
|
|
9929
9983
|
const now = /* @__PURE__ */ new Date();
|
|
9930
9984
|
const timestamp = now.toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
9931
9985
|
const today = now.toISOString().split("T")[0];
|
|
@@ -9948,9 +10002,9 @@ ${discovered.join("\n")}
|
|
|
9948
10002
|
let existingMemoriesContext = "";
|
|
9949
10003
|
const existingFiles = [];
|
|
9950
10004
|
try {
|
|
9951
|
-
const files = (0,
|
|
10005
|
+
const files = (0, import_fs10.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md"));
|
|
9952
10006
|
for (const f of files) {
|
|
9953
|
-
const content = (0,
|
|
10007
|
+
const content = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir, f), "utf-8");
|
|
9954
10008
|
const descMatch = content.match(/description:\s*(.+)/);
|
|
9955
10009
|
const desc = descMatch ? descMatch[1].trim() : "(no description)";
|
|
9956
10010
|
existingFiles.push(f);
|
|
@@ -10060,10 +10114,18 @@ ${rawInput.slice(0, SESSION_SUMMARY_MAX_CHARS)}`;
|
|
|
10060
10114
|
summaryBody = raw;
|
|
10061
10115
|
} else {
|
|
10062
10116
|
rawLlmResponse = raw;
|
|
10063
|
-
const hasSummaryLine = /^SUMMARY:\s*.+/m.test(raw);
|
|
10064
10117
|
const hasSectionHeader = /^##\s+\w/m.test(raw);
|
|
10065
|
-
if (!
|
|
10118
|
+
if (!hasSectionHeader) {
|
|
10066
10119
|
process.stderr.write("[gossipcat] Session summary missing required structure, using raw fallback\n");
|
|
10120
|
+
try {
|
|
10121
|
+
const debugPath = (0, import_path11.join)(memDir, "last-malformed-summary.txt");
|
|
10122
|
+
(0, import_fs10.writeFileSync)(debugPath, `# Malformed session summary @ ${timestamp}
|
|
10123
|
+
# No ## header found in LLM output.
|
|
10124
|
+
|
|
10125
|
+
---
|
|
10126
|
+
${raw}`);
|
|
10127
|
+
} catch {
|
|
10128
|
+
}
|
|
10067
10129
|
summaryBody = `> \u26A0\uFE0F LLM summary malformed \u2014 raw data below.
|
|
10068
10130
|
|
|
10069
10131
|
${rawInput.slice(0, SESSION_SUMMARY_MAX_CHARS)}`;
|
|
@@ -10105,15 +10167,15 @@ ${rawInput.slice(0, SESSION_SUMMARY_MAX_CHARS)}`;
|
|
|
10105
10167
|
if (staleFiles.length > 0) {
|
|
10106
10168
|
for (const sf of staleFiles) {
|
|
10107
10169
|
try {
|
|
10108
|
-
const filePath = (0,
|
|
10109
|
-
let fileContent = (0,
|
|
10170
|
+
const filePath = (0, import_path11.join)(knowledgeDir, sf);
|
|
10171
|
+
let fileContent = (0, import_fs10.readFileSync)(filePath, "utf-8");
|
|
10110
10172
|
fileContent = fileContent.replace(/importance:\s*[\d.]+/, "importance: 0.1");
|
|
10111
10173
|
if (!/status:/.test(fileContent)) {
|
|
10112
10174
|
fileContent = fileContent.replace(/\n---/, "\nstatus: shipped\n---");
|
|
10113
10175
|
} else {
|
|
10114
10176
|
fileContent = fileContent.replace(/status:\s*.+/, "status: shipped");
|
|
10115
10177
|
}
|
|
10116
|
-
(0,
|
|
10178
|
+
(0, import_fs10.writeFileSync)(filePath, fileContent);
|
|
10117
10179
|
process.stderr.write(`[gossipcat] \u{1F5DC}\uFE0F Marked stale: ${sf}
|
|
10118
10180
|
`);
|
|
10119
10181
|
} catch {
|
|
@@ -10134,8 +10196,8 @@ ${rawInput.slice(0, SESSION_SUMMARY_MAX_CHARS)}`;
|
|
|
10134
10196
|
"",
|
|
10135
10197
|
summaryBody
|
|
10136
10198
|
].filter((l) => l !== "").join("\n");
|
|
10137
|
-
(0,
|
|
10138
|
-
const nextSessionPath = (0,
|
|
10199
|
+
(0, import_fs10.writeFileSync)((0, import_path11.join)(knowledgeDir, filename), content);
|
|
10200
|
+
const nextSessionPath = (0, import_path11.join)(this.projectRoot, ".gossip", "next-session.md");
|
|
10139
10201
|
const openMatch = summaryBody.match(/##\s+Open[^\n]*\n([\s\S]*?)(?=\n##|\s*$)/i);
|
|
10140
10202
|
const NEXT_SESSION_MAX_CHARS = 1500;
|
|
10141
10203
|
const nextSessionContent = openMatch ? `# Next Session
|
|
@@ -10145,11 +10207,11 @@ ${openMatch[0].trim()}
|
|
|
10145
10207
|
|
|
10146
10208
|
${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
|
|
10147
10209
|
`;
|
|
10148
|
-
(0,
|
|
10149
|
-
const migrationTasksPath = (0,
|
|
10150
|
-
if ((0,
|
|
10210
|
+
(0, import_fs10.writeFileSync)(nextSessionPath, nextSessionContent);
|
|
10211
|
+
const migrationTasksPath = (0, import_path11.join)(memDir, "tasks.jsonl");
|
|
10212
|
+
if ((0, import_fs10.existsSync)(migrationTasksPath)) {
|
|
10151
10213
|
try {
|
|
10152
|
-
const mLines = (0,
|
|
10214
|
+
const mLines = (0, import_fs10.readFileSync)(migrationTasksPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
10153
10215
|
let migrated = false;
|
|
10154
10216
|
const fixed = mLines.map((line) => {
|
|
10155
10217
|
try {
|
|
@@ -10163,7 +10225,7 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
|
|
|
10163
10225
|
return line;
|
|
10164
10226
|
}
|
|
10165
10227
|
});
|
|
10166
|
-
if (migrated) (0,
|
|
10228
|
+
if (migrated) (0, import_fs10.writeFileSync)(migrationTasksPath, fixed.join("\n") + "\n");
|
|
10167
10229
|
} catch {
|
|
10168
10230
|
}
|
|
10169
10231
|
}
|
|
@@ -10188,10 +10250,10 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
|
|
|
10188
10250
|
/** Shared warmth-aware pruning — evicts lowest-warmth files, respects pinned */
|
|
10189
10251
|
pruneKnowledgeDir(knowledgeDir, maxFiles) {
|
|
10190
10252
|
try {
|
|
10191
|
-
const existing = (0,
|
|
10253
|
+
const existing = (0, import_fs10.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md")).sort();
|
|
10192
10254
|
if (existing.length >= maxFiles) {
|
|
10193
10255
|
const scored = existing.map((f) => {
|
|
10194
|
-
const content = (0,
|
|
10256
|
+
const content = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir, f), "utf-8");
|
|
10195
10257
|
const importance = parseFloat(content.match(/importance:\s*([\d.]+)/)?.[1] ?? "0.5");
|
|
10196
10258
|
const isPinned = /pinned:\s*true/i.test(content);
|
|
10197
10259
|
const ts = f.slice(0, 19).replace(/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})/, "$1T$2:$3:$4");
|
|
@@ -10210,10 +10272,10 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
|
|
|
10210
10272
|
);
|
|
10211
10273
|
}
|
|
10212
10274
|
for (const item of toEvict) {
|
|
10213
|
-
(0,
|
|
10275
|
+
(0, import_fs10.unlinkSync)((0, import_path11.join)(knowledgeDir, item.file));
|
|
10214
10276
|
}
|
|
10215
10277
|
}
|
|
10216
|
-
const sessionFiles = (0,
|
|
10278
|
+
const sessionFiles = (0, import_fs10.readdirSync)(knowledgeDir).filter((f) => f.endsWith("-session.md")).sort();
|
|
10217
10279
|
const MAX_SESSION_FILES = 5;
|
|
10218
10280
|
const SESSION_TTL_DAYS = 14;
|
|
10219
10281
|
for (const sf of sessionFiles) {
|
|
@@ -10221,10 +10283,10 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
|
|
|
10221
10283
|
const ts = sf.slice(0, 19).replace(/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})/, "$1T$2:$3:$4");
|
|
10222
10284
|
const days = (Date.now() - new Date(ts).getTime()) / 864e5;
|
|
10223
10285
|
if (days > SESSION_TTL_DAYS) {
|
|
10224
|
-
const sfPath = (0,
|
|
10225
|
-
const sfContent = (0,
|
|
10286
|
+
const sfPath = (0, import_path11.join)(knowledgeDir, sf);
|
|
10287
|
+
const sfContent = (0, import_fs10.readFileSync)(sfPath, "utf-8");
|
|
10226
10288
|
if (!/importance:\s*0\.1/.test(sfContent)) {
|
|
10227
|
-
(0,
|
|
10289
|
+
(0, import_fs10.writeFileSync)(sfPath, sfContent.replace(/importance:\s*[\d.]+/, "importance: 0.1"));
|
|
10228
10290
|
}
|
|
10229
10291
|
}
|
|
10230
10292
|
} catch {
|
|
@@ -10235,7 +10297,7 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
|
|
|
10235
10297
|
const bodies = [];
|
|
10236
10298
|
for (const sf of toCompact) {
|
|
10237
10299
|
try {
|
|
10238
|
-
const sfContent = (0,
|
|
10300
|
+
const sfContent = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir, sf), "utf-8");
|
|
10239
10301
|
const body = sfContent.split("---").slice(2).join("---").trim();
|
|
10240
10302
|
if (body) bodies.push(body.slice(0, 800));
|
|
10241
10303
|
} catch {
|
|
@@ -10255,10 +10317,10 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
|
|
|
10255
10317
|
"",
|
|
10256
10318
|
...bodies
|
|
10257
10319
|
].join("\n").slice(0, 3e3);
|
|
10258
|
-
(0,
|
|
10320
|
+
(0, import_fs10.writeFileSync)((0, import_path11.join)(knowledgeDir, digestName), digestContent);
|
|
10259
10321
|
for (const sf of toCompact) {
|
|
10260
10322
|
try {
|
|
10261
|
-
(0,
|
|
10323
|
+
(0, import_fs10.unlinkSync)((0, import_path11.join)(knowledgeDir, sf));
|
|
10262
10324
|
} catch {
|
|
10263
10325
|
}
|
|
10264
10326
|
}
|
|
@@ -10441,7 +10503,7 @@ ${result}`;
|
|
|
10441
10503
|
writeConsensusKnowledge(agentId, findings) {
|
|
10442
10504
|
if (findings.length === 0) return;
|
|
10443
10505
|
const memDir = this.ensureDirs(agentId);
|
|
10444
|
-
const knowledgeDir = (0,
|
|
10506
|
+
const knowledgeDir = (0, import_path11.join)(memDir, "knowledge");
|
|
10445
10507
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
10446
10508
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
10447
10509
|
const filename = `${timestamp}-consensus.md`;
|
|
@@ -10471,7 +10533,7 @@ ${result}`;
|
|
|
10471
10533
|
})
|
|
10472
10534
|
].join("\n");
|
|
10473
10535
|
this.pruneKnowledgeDir(knowledgeDir, 25);
|
|
10474
|
-
(0,
|
|
10536
|
+
(0, import_fs10.writeFileSync)((0, import_path11.join)(knowledgeDir, filename), content);
|
|
10475
10537
|
}
|
|
10476
10538
|
/**
|
|
10477
10539
|
* Update task memory importance based on consensus signals.
|
|
@@ -10494,14 +10556,14 @@ ${result}`;
|
|
|
10494
10556
|
taskAdj.set(s.taskId, (taskAdj.get(s.taskId) ?? 0) + weight);
|
|
10495
10557
|
}
|
|
10496
10558
|
for (const [agentId, taskAdjustments] of adjustments) {
|
|
10497
|
-
const memDir = (0,
|
|
10498
|
-
const tasksPath = (0,
|
|
10499
|
-
const lockPath = (0,
|
|
10500
|
-
if (!(0,
|
|
10501
|
-
if ((0,
|
|
10559
|
+
const memDir = (0, import_path11.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
|
|
10560
|
+
const tasksPath = (0, import_path11.join)(memDir, "tasks.jsonl");
|
|
10561
|
+
const lockPath = (0, import_path11.join)(memDir, "tasks.jsonl.lock");
|
|
10562
|
+
if (!(0, import_fs10.existsSync)(tasksPath)) continue;
|
|
10563
|
+
if ((0, import_fs10.existsSync)(lockPath)) continue;
|
|
10502
10564
|
try {
|
|
10503
|
-
(0,
|
|
10504
|
-
const lines = (0,
|
|
10565
|
+
(0, import_fs10.writeFileSync)(lockPath, `${Date.now()}`);
|
|
10566
|
+
const lines = (0, import_fs10.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
10505
10567
|
let modified = false;
|
|
10506
10568
|
const updated = lines.map((line) => {
|
|
10507
10569
|
try {
|
|
@@ -10518,12 +10580,12 @@ ${result}`;
|
|
|
10518
10580
|
}
|
|
10519
10581
|
});
|
|
10520
10582
|
if (modified) {
|
|
10521
|
-
(0,
|
|
10583
|
+
(0, import_fs10.writeFileSync)(tasksPath, updated.join("\n") + "\n");
|
|
10522
10584
|
}
|
|
10523
10585
|
} catch {
|
|
10524
10586
|
} finally {
|
|
10525
10587
|
try {
|
|
10526
|
-
(0,
|
|
10588
|
+
(0, import_fs10.unlinkSync)(lockPath);
|
|
10527
10589
|
} catch {
|
|
10528
10590
|
}
|
|
10529
10591
|
}
|
|
@@ -10533,13 +10595,13 @@ ${result}`;
|
|
|
10533
10595
|
const memDir = this.getMemDir(agentId);
|
|
10534
10596
|
const parts = [`# Agent Memory \u2014 ${agentId}
|
|
10535
10597
|
`];
|
|
10536
|
-
const knowledgeDir = (0,
|
|
10537
|
-
if ((0,
|
|
10538
|
-
const files = (0,
|
|
10598
|
+
const knowledgeDir = (0, import_path11.join)(memDir, "knowledge");
|
|
10599
|
+
if ((0, import_fs10.existsSync)(knowledgeDir)) {
|
|
10600
|
+
const files = (0, import_fs10.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md")).sort().reverse();
|
|
10539
10601
|
if (files.length > 0) {
|
|
10540
10602
|
parts.push("## Knowledge (most recent first)");
|
|
10541
10603
|
for (const file2 of files) {
|
|
10542
|
-
const content = (0,
|
|
10604
|
+
const content = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir, file2), "utf-8");
|
|
10543
10605
|
const descMatch = content.match(/description:\s*(.+)/);
|
|
10544
10606
|
const desc = descMatch ? descMatch[1].trim() : file2.replace(".md", "");
|
|
10545
10607
|
parts.push(`- [${file2.replace(".md", "")}](knowledge/${file2}) \u2014 ${desc}`);
|
|
@@ -10547,17 +10609,17 @@ ${result}`;
|
|
|
10547
10609
|
parts.push("");
|
|
10548
10610
|
}
|
|
10549
10611
|
}
|
|
10550
|
-
const calPath = (0,
|
|
10551
|
-
if ((0,
|
|
10552
|
-
const content = (0,
|
|
10612
|
+
const calPath = (0, import_path11.join)(memDir, "calibration", "accuracy.md");
|
|
10613
|
+
if ((0, import_fs10.existsSync)(calPath)) {
|
|
10614
|
+
const content = (0, import_fs10.readFileSync)(calPath, "utf-8");
|
|
10553
10615
|
const descMatch = content.match(/description:\s*(.+)/);
|
|
10554
10616
|
parts.push("## Calibration");
|
|
10555
10617
|
parts.push(`- [accuracy](calibration/accuracy.md) \u2014 ${descMatch ? descMatch[1].trim() : "accuracy data"}`);
|
|
10556
10618
|
parts.push("");
|
|
10557
10619
|
}
|
|
10558
|
-
const tasksPath = (0,
|
|
10559
|
-
if ((0,
|
|
10560
|
-
const lines = (0,
|
|
10620
|
+
const tasksPath = (0, import_path11.join)(memDir, "tasks.jsonl");
|
|
10621
|
+
if ((0, import_fs10.existsSync)(tasksPath)) {
|
|
10622
|
+
const lines = (0, import_fs10.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
10561
10623
|
const recent = lines.slice(-5).reverse();
|
|
10562
10624
|
if (recent.length > 0) {
|
|
10563
10625
|
parts.push("## Recent Tasks");
|
|
@@ -10574,12 +10636,12 @@ ${result}`;
|
|
|
10574
10636
|
}
|
|
10575
10637
|
}
|
|
10576
10638
|
try {
|
|
10577
|
-
const knowledgeDir2 = (0,
|
|
10578
|
-
if ((0,
|
|
10579
|
-
const knowledgeFiles = (0,
|
|
10639
|
+
const knowledgeDir2 = (0, import_path11.join)(memDir, "knowledge");
|
|
10640
|
+
if ((0, import_fs10.existsSync)(knowledgeDir2)) {
|
|
10641
|
+
const knowledgeFiles = (0, import_fs10.readdirSync)(knowledgeDir2).filter((f) => f.endsWith(".md")).sort().reverse().slice(0, 5);
|
|
10580
10642
|
const patterns = [];
|
|
10581
10643
|
for (const kf of knowledgeFiles) {
|
|
10582
|
-
const kContent = (0,
|
|
10644
|
+
const kContent = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir2, kf), "utf-8");
|
|
10583
10645
|
const decisionsMatch = kContent.match(/Decisions: (.+)/);
|
|
10584
10646
|
if (decisionsMatch) patterns.push(decisionsMatch[1].trim());
|
|
10585
10647
|
const failuresMatch = kContent.match(/Failures: (.+)/);
|
|
@@ -10591,19 +10653,19 @@ ${result}`;
|
|
|
10591
10653
|
}
|
|
10592
10654
|
} catch {
|
|
10593
10655
|
}
|
|
10594
|
-
(0,
|
|
10656
|
+
(0, import_fs10.writeFileSync)((0, import_path11.join)(memDir, "MEMORY.md"), parts.join("\n"));
|
|
10595
10657
|
}
|
|
10596
10658
|
};
|
|
10597
10659
|
}
|
|
10598
10660
|
});
|
|
10599
10661
|
|
|
10600
10662
|
// packages/orchestrator/src/task-graph.ts
|
|
10601
|
-
var
|
|
10663
|
+
var import_fs11, import_path12, MAX_SCAN_LINES, TaskGraph;
|
|
10602
10664
|
var init_task_graph = __esm({
|
|
10603
10665
|
"packages/orchestrator/src/task-graph.ts"() {
|
|
10604
10666
|
"use strict";
|
|
10605
|
-
|
|
10606
|
-
|
|
10667
|
+
import_fs11 = require("fs");
|
|
10668
|
+
import_path12 = require("path");
|
|
10607
10669
|
MAX_SCAN_LINES = 1e3;
|
|
10608
10670
|
TaskGraph = class {
|
|
10609
10671
|
graphPath;
|
|
@@ -10613,14 +10675,14 @@ var init_task_graph = __esm({
|
|
|
10613
10675
|
// taskId → last event line number
|
|
10614
10676
|
eventCount = 0;
|
|
10615
10677
|
constructor(projectRoot) {
|
|
10616
|
-
const gossipDir2 = (0,
|
|
10617
|
-
(0,
|
|
10618
|
-
this.graphPath = (0,
|
|
10619
|
-
this.syncMetaPath = (0,
|
|
10620
|
-
this.indexPath = (0,
|
|
10678
|
+
const gossipDir2 = (0, import_path12.join)(projectRoot, ".gossip");
|
|
10679
|
+
(0, import_fs11.mkdirSync)(gossipDir2, { recursive: true });
|
|
10680
|
+
this.graphPath = (0, import_path12.join)(gossipDir2, "task-graph.jsonl");
|
|
10681
|
+
this.syncMetaPath = (0, import_path12.join)(gossipDir2, "task-graph-sync.json");
|
|
10682
|
+
this.indexPath = (0, import_path12.join)(gossipDir2, "task-graph-index.json");
|
|
10621
10683
|
this.loadIndex();
|
|
10622
|
-
if ((0,
|
|
10623
|
-
const buf = (0,
|
|
10684
|
+
if ((0, import_fs11.existsSync)(this.graphPath)) {
|
|
10685
|
+
const buf = (0, import_fs11.readFileSync)(this.graphPath);
|
|
10624
10686
|
let count = 0;
|
|
10625
10687
|
for (let i = 0; i < buf.length; i++) {
|
|
10626
10688
|
if (buf[i] === 10) count++;
|
|
@@ -10629,9 +10691,9 @@ var init_task_graph = __esm({
|
|
|
10629
10691
|
}
|
|
10630
10692
|
}
|
|
10631
10693
|
loadIndex() {
|
|
10632
|
-
if ((0,
|
|
10694
|
+
if ((0, import_fs11.existsSync)(this.indexPath)) {
|
|
10633
10695
|
try {
|
|
10634
|
-
const data = JSON.parse((0,
|
|
10696
|
+
const data = JSON.parse((0, import_fs11.readFileSync)(this.indexPath, "utf-8"));
|
|
10635
10697
|
this.index = new Map(Object.entries(data).map(([k, v]) => [k, Number(v)]));
|
|
10636
10698
|
} catch {
|
|
10637
10699
|
}
|
|
@@ -10639,10 +10701,10 @@ var init_task_graph = __esm({
|
|
|
10639
10701
|
}
|
|
10640
10702
|
/** Save index to disk (call explicitly, not on every append) */
|
|
10641
10703
|
flushIndex() {
|
|
10642
|
-
(0,
|
|
10704
|
+
(0, import_fs11.writeFileSync)(this.indexPath, JSON.stringify(Object.fromEntries(this.index)));
|
|
10643
10705
|
}
|
|
10644
10706
|
appendEvent(event) {
|
|
10645
|
-
(0,
|
|
10707
|
+
(0, import_fs11.appendFileSync)(this.graphPath, JSON.stringify(event) + "\n");
|
|
10646
10708
|
if ("taskId" in event) {
|
|
10647
10709
|
this.index.set(event.taskId, this.eventCount);
|
|
10648
10710
|
}
|
|
@@ -10724,8 +10786,8 @@ var init_task_graph = __esm({
|
|
|
10724
10786
|
}
|
|
10725
10787
|
// ── Read methods ─────────────────────────────────────────────────────
|
|
10726
10788
|
readEvents() {
|
|
10727
|
-
if (!(0,
|
|
10728
|
-
const content = (0,
|
|
10789
|
+
if (!(0, import_fs11.existsSync)(this.graphPath)) return [];
|
|
10790
|
+
const content = (0, import_fs11.readFileSync)(this.graphPath, "utf-8");
|
|
10729
10791
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
10730
10792
|
const tail = lines.slice(-MAX_SCAN_LINES);
|
|
10731
10793
|
return tail.map((line) => {
|
|
@@ -10807,14 +10869,14 @@ var init_task_graph = __esm({
|
|
|
10807
10869
|
return this.eventCount;
|
|
10808
10870
|
}
|
|
10809
10871
|
getSyncMeta() {
|
|
10810
|
-
if (!(0,
|
|
10872
|
+
if (!(0, import_fs11.existsSync)(this.syncMetaPath)) {
|
|
10811
10873
|
return { lastSync: "", lastSyncEventCount: 0 };
|
|
10812
10874
|
}
|
|
10813
|
-
return JSON.parse((0,
|
|
10875
|
+
return JSON.parse((0, import_fs11.readFileSync)(this.syncMetaPath, "utf-8"));
|
|
10814
10876
|
}
|
|
10815
10877
|
updateSyncMeta(meta3) {
|
|
10816
10878
|
const current = this.getSyncMeta();
|
|
10817
|
-
(0,
|
|
10879
|
+
(0, import_fs11.writeFileSync)(this.syncMetaPath, JSON.stringify({ ...current, ...meta3 }, null, 2));
|
|
10818
10880
|
}
|
|
10819
10881
|
getUnsynced(lastSyncTimestamp) {
|
|
10820
10882
|
if (!lastSyncTimestamp) return this.readEvents();
|
|
@@ -10825,12 +10887,12 @@ var init_task_graph = __esm({
|
|
|
10825
10887
|
});
|
|
10826
10888
|
|
|
10827
10889
|
// packages/orchestrator/src/skill-catalog.ts
|
|
10828
|
-
var
|
|
10890
|
+
var import_fs12, import_path13, SkillCatalog;
|
|
10829
10891
|
var init_skill_catalog = __esm({
|
|
10830
10892
|
"packages/orchestrator/src/skill-catalog.ts"() {
|
|
10831
10893
|
"use strict";
|
|
10832
|
-
|
|
10833
|
-
|
|
10894
|
+
import_fs12 = require("fs");
|
|
10895
|
+
import_path13 = require("path");
|
|
10834
10896
|
init_skill_name();
|
|
10835
10897
|
init_skill_parser();
|
|
10836
10898
|
SkillCatalog = class {
|
|
@@ -10839,11 +10901,11 @@ var init_skill_catalog = __esm({
|
|
|
10839
10901
|
projectSkillsDir;
|
|
10840
10902
|
projectFileMtimes = /* @__PURE__ */ new Map();
|
|
10841
10903
|
constructor(projectRoot, catalogPath) {
|
|
10842
|
-
const defaultPath = catalogPath || (0,
|
|
10843
|
-
this.defaultSkillsDir = (0,
|
|
10844
|
-
this.projectSkillsDir = projectRoot ? (0,
|
|
10904
|
+
const defaultPath = catalogPath || (0, import_path13.resolve)(__dirname, "default-skills", "catalog.json");
|
|
10905
|
+
this.defaultSkillsDir = (0, import_path13.resolve)(__dirname, "default-skills");
|
|
10906
|
+
this.projectSkillsDir = projectRoot ? (0, import_path13.join)(projectRoot, ".gossip", "skills") : null;
|
|
10845
10907
|
try {
|
|
10846
|
-
const raw = (0,
|
|
10908
|
+
const raw = (0, import_fs12.readFileSync)(defaultPath, "utf-8");
|
|
10847
10909
|
const data = JSON.parse(raw);
|
|
10848
10910
|
this.entries = data.skills.map((s) => ({
|
|
10849
10911
|
...s,
|
|
@@ -10884,7 +10946,7 @@ var init_skill_catalog = __esm({
|
|
|
10884
10946
|
}
|
|
10885
10947
|
validate() {
|
|
10886
10948
|
const issues = [];
|
|
10887
|
-
const mdFiles = (0,
|
|
10949
|
+
const mdFiles = (0, import_fs12.readdirSync)(this.defaultSkillsDir).filter((f) => f.endsWith(".md")).map((f) => normalizeSkillName(f.replace(".md", "")));
|
|
10888
10950
|
for (const file2 of mdFiles) {
|
|
10889
10951
|
if (!this.entries.find((e) => e.name === file2)) {
|
|
10890
10952
|
issues.push(`Skill file '${file2}' has no catalog entry`);
|
|
@@ -10893,15 +10955,15 @@ var init_skill_catalog = __esm({
|
|
|
10893
10955
|
return issues;
|
|
10894
10956
|
}
|
|
10895
10957
|
loadProjectSkills() {
|
|
10896
|
-
if (!this.projectSkillsDir || !(0,
|
|
10897
|
-
const files = (0,
|
|
10958
|
+
if (!this.projectSkillsDir || !(0, import_fs12.existsSync)(this.projectSkillsDir)) return;
|
|
10959
|
+
const files = (0, import_fs12.readdirSync)(this.projectSkillsDir).filter((f) => f.endsWith(".md"));
|
|
10898
10960
|
const newMtimes = /* @__PURE__ */ new Map();
|
|
10899
10961
|
for (const file2 of files) {
|
|
10900
|
-
const filePath = (0,
|
|
10962
|
+
const filePath = (0, import_path13.join)(this.projectSkillsDir, file2);
|
|
10901
10963
|
try {
|
|
10902
|
-
const mtime = (0,
|
|
10964
|
+
const mtime = (0, import_fs12.statSync)(filePath).mtimeMs;
|
|
10903
10965
|
newMtimes.set(file2, mtime);
|
|
10904
|
-
const content = (0,
|
|
10966
|
+
const content = (0, import_fs12.readFileSync)(filePath, "utf-8");
|
|
10905
10967
|
const fm = parseSkillFrontmatter(content);
|
|
10906
10968
|
if (!fm) continue;
|
|
10907
10969
|
const entry = {
|
|
@@ -10923,14 +10985,14 @@ var init_skill_catalog = __esm({
|
|
|
10923
10985
|
this.projectFileMtimes = newMtimes;
|
|
10924
10986
|
}
|
|
10925
10987
|
reloadIfChanged() {
|
|
10926
|
-
if (!this.projectSkillsDir || !(0,
|
|
10927
|
-
const files = (0,
|
|
10988
|
+
if (!this.projectSkillsDir || !(0, import_fs12.existsSync)(this.projectSkillsDir)) return;
|
|
10989
|
+
const files = (0, import_fs12.readdirSync)(this.projectSkillsDir).filter((f) => f.endsWith(".md"));
|
|
10928
10990
|
let changed = files.length !== this.projectFileMtimes.size;
|
|
10929
10991
|
if (!changed) {
|
|
10930
10992
|
for (const file2 of files) {
|
|
10931
|
-
const filePath = (0,
|
|
10993
|
+
const filePath = (0, import_path13.join)(this.projectSkillsDir, file2);
|
|
10932
10994
|
try {
|
|
10933
|
-
const mtime = (0,
|
|
10995
|
+
const mtime = (0, import_fs12.statSync)(filePath).mtimeMs;
|
|
10934
10996
|
if (mtime !== this.projectFileMtimes.get(file2)) {
|
|
10935
10997
|
changed = true;
|
|
10936
10998
|
break;
|
|
@@ -10951,12 +11013,12 @@ var init_skill_catalog = __esm({
|
|
|
10951
11013
|
});
|
|
10952
11014
|
|
|
10953
11015
|
// packages/orchestrator/src/skill-gap-tracker.ts
|
|
10954
|
-
var
|
|
11016
|
+
var import_fs13, import_path14, MAX_LOG_LINES, TRUNCATE_TO, SkillGapTracker;
|
|
10955
11017
|
var init_skill_gap_tracker = __esm({
|
|
10956
11018
|
"packages/orchestrator/src/skill-gap-tracker.ts"() {
|
|
10957
11019
|
"use strict";
|
|
10958
|
-
|
|
10959
|
-
|
|
11020
|
+
import_fs13 = require("fs");
|
|
11021
|
+
import_path14 = require("path");
|
|
10960
11022
|
init_skill_name();
|
|
10961
11023
|
MAX_LOG_LINES = 5e3;
|
|
10962
11024
|
TRUNCATE_TO = 1e3;
|
|
@@ -10965,8 +11027,8 @@ var init_skill_gap_tracker = __esm({
|
|
|
10965
11027
|
resolutionsPath;
|
|
10966
11028
|
resolutionsCache = null;
|
|
10967
11029
|
constructor(projectRoot) {
|
|
10968
|
-
this.gapLogPath = (0,
|
|
10969
|
-
this.resolutionsPath = (0,
|
|
11030
|
+
this.gapLogPath = (0, import_path14.join)(projectRoot, ".gossip", "skill-gaps.jsonl");
|
|
11031
|
+
this.resolutionsPath = (0, import_path14.join)(projectRoot, ".gossip", "skill-resolutions.json");
|
|
10970
11032
|
this.migrateResolutions();
|
|
10971
11033
|
}
|
|
10972
11034
|
checkThresholds() {
|
|
@@ -10994,9 +11056,9 @@ var init_skill_gap_tracker = __esm({
|
|
|
10994
11056
|
const normalized = normalizeSkillName(skillName);
|
|
10995
11057
|
const resolutions = this.loadResolutions();
|
|
10996
11058
|
resolutions[normalized] = (/* @__PURE__ */ new Date()).toISOString();
|
|
10997
|
-
const dir = (0,
|
|
10998
|
-
if (!(0,
|
|
10999
|
-
(0,
|
|
11059
|
+
const dir = (0, import_path14.join)(this.resolutionsPath, "..");
|
|
11060
|
+
if (!(0, import_fs13.existsSync)(dir)) (0, import_fs13.mkdirSync)(dir, { recursive: true });
|
|
11061
|
+
(0, import_fs13.writeFileSync)(this.resolutionsPath, JSON.stringify(resolutions, null, 2));
|
|
11000
11062
|
this.resolutionsCache = resolutions;
|
|
11001
11063
|
}
|
|
11002
11064
|
getSuggestionsSince(agentId, sinceMs) {
|
|
@@ -11005,9 +11067,9 @@ var init_skill_gap_tracker = __esm({
|
|
|
11005
11067
|
);
|
|
11006
11068
|
}
|
|
11007
11069
|
appendSuggestion(suggestion) {
|
|
11008
|
-
const dir = (0,
|
|
11009
|
-
if (!(0,
|
|
11010
|
-
(0,
|
|
11070
|
+
const dir = (0, import_path14.join)(this.gapLogPath, "..");
|
|
11071
|
+
if (!(0, import_fs13.existsSync)(dir)) (0, import_fs13.mkdirSync)(dir, { recursive: true });
|
|
11072
|
+
(0, import_fs13.appendFileSync)(this.gapLogPath, JSON.stringify(suggestion) + "\n");
|
|
11011
11073
|
this.truncateIfNeeded();
|
|
11012
11074
|
}
|
|
11013
11075
|
getPendingSkills() {
|
|
@@ -11035,9 +11097,9 @@ var init_skill_gap_tracker = __esm({
|
|
|
11035
11097
|
);
|
|
11036
11098
|
}
|
|
11037
11099
|
readSuggestions() {
|
|
11038
|
-
if (!(0,
|
|
11100
|
+
if (!(0, import_fs13.existsSync)(this.gapLogPath)) return [];
|
|
11039
11101
|
try {
|
|
11040
|
-
const lines = (0,
|
|
11102
|
+
const lines = (0, import_fs13.readFileSync)(this.gapLogPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
11041
11103
|
return lines.map((line) => {
|
|
11042
11104
|
try {
|
|
11043
11105
|
return JSON.parse(line);
|
|
@@ -11051,12 +11113,12 @@ var init_skill_gap_tracker = __esm({
|
|
|
11051
11113
|
}
|
|
11052
11114
|
loadResolutions() {
|
|
11053
11115
|
if (this.resolutionsCache) return this.resolutionsCache;
|
|
11054
|
-
if (!(0,
|
|
11116
|
+
if (!(0, import_fs13.existsSync)(this.resolutionsPath)) {
|
|
11055
11117
|
this.resolutionsCache = {};
|
|
11056
11118
|
return this.resolutionsCache;
|
|
11057
11119
|
}
|
|
11058
11120
|
try {
|
|
11059
|
-
this.resolutionsCache = JSON.parse((0,
|
|
11121
|
+
this.resolutionsCache = JSON.parse((0, import_fs13.readFileSync)(this.resolutionsPath, "utf-8"));
|
|
11060
11122
|
return this.resolutionsCache;
|
|
11061
11123
|
} catch {
|
|
11062
11124
|
this.resolutionsCache = {};
|
|
@@ -11064,10 +11126,10 @@ var init_skill_gap_tracker = __esm({
|
|
|
11064
11126
|
}
|
|
11065
11127
|
}
|
|
11066
11128
|
migrateResolutions() {
|
|
11067
|
-
if ((0,
|
|
11068
|
-
if (!(0,
|
|
11129
|
+
if ((0, import_fs13.existsSync)(this.resolutionsPath)) return;
|
|
11130
|
+
if (!(0, import_fs13.existsSync)(this.gapLogPath)) return;
|
|
11069
11131
|
try {
|
|
11070
|
-
const lines = (0,
|
|
11132
|
+
const lines = (0, import_fs13.readFileSync)(this.gapLogPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
11071
11133
|
const resolutions = {};
|
|
11072
11134
|
for (const line of lines) {
|
|
11073
11135
|
try {
|
|
@@ -11079,21 +11141,21 @@ var init_skill_gap_tracker = __esm({
|
|
|
11079
11141
|
}
|
|
11080
11142
|
}
|
|
11081
11143
|
if (Object.keys(resolutions).length > 0) {
|
|
11082
|
-
const dir = (0,
|
|
11083
|
-
if (!(0,
|
|
11084
|
-
(0,
|
|
11144
|
+
const dir = (0, import_path14.join)(this.resolutionsPath, "..");
|
|
11145
|
+
if (!(0, import_fs13.existsSync)(dir)) (0, import_fs13.mkdirSync)(dir, { recursive: true });
|
|
11146
|
+
(0, import_fs13.writeFileSync)(this.resolutionsPath, JSON.stringify(resolutions, null, 2));
|
|
11085
11147
|
this.resolutionsCache = resolutions;
|
|
11086
11148
|
}
|
|
11087
11149
|
} catch {
|
|
11088
11150
|
}
|
|
11089
11151
|
}
|
|
11090
11152
|
truncateIfNeeded() {
|
|
11091
|
-
if (!(0,
|
|
11153
|
+
if (!(0, import_fs13.existsSync)(this.gapLogPath)) return;
|
|
11092
11154
|
try {
|
|
11093
|
-
const content = (0,
|
|
11155
|
+
const content = (0, import_fs13.readFileSync)(this.gapLogPath, "utf-8");
|
|
11094
11156
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
11095
11157
|
if (lines.length > MAX_LOG_LINES) {
|
|
11096
|
-
(0,
|
|
11158
|
+
(0, import_fs13.writeFileSync)(this.gapLogPath, lines.slice(-TRUNCATE_TO).join("\n") + "\n");
|
|
11097
11159
|
}
|
|
11098
11160
|
} catch {
|
|
11099
11161
|
}
|
|
@@ -11103,12 +11165,12 @@ var init_skill_gap_tracker = __esm({
|
|
|
11103
11165
|
});
|
|
11104
11166
|
|
|
11105
11167
|
// packages/orchestrator/src/scope-tracker.ts
|
|
11106
|
-
var
|
|
11168
|
+
var import_path15, import_fs14, ScopeTracker;
|
|
11107
11169
|
var init_scope_tracker = __esm({
|
|
11108
11170
|
"packages/orchestrator/src/scope-tracker.ts"() {
|
|
11109
11171
|
"use strict";
|
|
11110
|
-
|
|
11111
|
-
|
|
11172
|
+
import_path15 = require("path");
|
|
11173
|
+
import_fs14 = require("fs");
|
|
11112
11174
|
ScopeTracker = class {
|
|
11113
11175
|
// taskId → scope (for release)
|
|
11114
11176
|
constructor(projectRoot) {
|
|
@@ -11119,15 +11181,15 @@ var init_scope_tracker = __esm({
|
|
|
11119
11181
|
taskToScope = /* @__PURE__ */ new Map();
|
|
11120
11182
|
normalize(scope) {
|
|
11121
11183
|
if (!scope || !scope.trim()) throw new Error("Scope must not be empty");
|
|
11122
|
-
const realRoot = (0,
|
|
11123
|
-
const abs = (0,
|
|
11184
|
+
const realRoot = (0, import_fs14.realpathSync)(this.projectRoot);
|
|
11185
|
+
const abs = (0, import_path15.resolve)(realRoot, scope);
|
|
11124
11186
|
let real;
|
|
11125
11187
|
try {
|
|
11126
|
-
real = (0,
|
|
11188
|
+
real = (0, import_fs14.realpathSync)(abs);
|
|
11127
11189
|
} catch {
|
|
11128
11190
|
real = abs;
|
|
11129
11191
|
}
|
|
11130
|
-
const rel = (0,
|
|
11192
|
+
const rel = (0, import_path15.relative)(realRoot, real);
|
|
11131
11193
|
if (rel.startsWith("..")) throw new Error(`Scope "${scope}" resolves outside project root`);
|
|
11132
11194
|
if (rel === "") throw new Error(`Scope "${scope}" resolves to project root \u2014 too broad`);
|
|
11133
11195
|
return rel.endsWith("/") ? rel : rel + "/";
|
|
@@ -11165,14 +11227,14 @@ var init_scope_tracker = __esm({
|
|
|
11165
11227
|
});
|
|
11166
11228
|
|
|
11167
11229
|
// packages/orchestrator/src/worktree-manager.ts
|
|
11168
|
-
var import_child_process3, import_util8, import_promises2,
|
|
11230
|
+
var import_child_process3, import_util8, import_promises2, import_path16, import_os, execFileAsync3, WorktreeManager;
|
|
11169
11231
|
var init_worktree_manager = __esm({
|
|
11170
11232
|
"packages/orchestrator/src/worktree-manager.ts"() {
|
|
11171
11233
|
"use strict";
|
|
11172
11234
|
import_child_process3 = require("child_process");
|
|
11173
11235
|
import_util8 = require("util");
|
|
11174
11236
|
import_promises2 = require("fs/promises");
|
|
11175
|
-
|
|
11237
|
+
import_path16 = require("path");
|
|
11176
11238
|
import_os = require("os");
|
|
11177
11239
|
execFileAsync3 = (0, import_util8.promisify)(import_child_process3.execFile);
|
|
11178
11240
|
WorktreeManager = class {
|
|
@@ -11181,7 +11243,7 @@ var init_worktree_manager = __esm({
|
|
|
11181
11243
|
}
|
|
11182
11244
|
async create(taskId) {
|
|
11183
11245
|
const branch = `gossip-${taskId}`;
|
|
11184
|
-
const wtPath = await (0, import_promises2.mkdtemp)((0,
|
|
11246
|
+
const wtPath = await (0, import_promises2.mkdtemp)((0, import_path16.join)((0, import_os.tmpdir)(), "gossip-wt-"));
|
|
11185
11247
|
await execFileAsync3("git", ["branch", branch, "HEAD"], { cwd: this.projectRoot });
|
|
11186
11248
|
try {
|
|
11187
11249
|
await execFileAsync3("git", ["worktree", "add", wtPath, branch], { cwd: this.projectRoot });
|
|
@@ -11256,12 +11318,13 @@ function getSeverityMultiplier(severity) {
|
|
|
11256
11318
|
function clamp(v, min, max) {
|
|
11257
11319
|
return Math.max(min, Math.min(max, v));
|
|
11258
11320
|
}
|
|
11259
|
-
var
|
|
11321
|
+
var import_fs15, import_path17, CIRCUIT_BREAKER_THRESHOLD, NEGATIVE_SIGNALS, SIGNAL_EXPIRY_DAYS, KNOWN_SIGNALS, SEVERITY_MULTIPLIER, PerformanceReader;
|
|
11260
11322
|
var init_performance_reader = __esm({
|
|
11261
11323
|
"packages/orchestrator/src/performance-reader.ts"() {
|
|
11262
11324
|
"use strict";
|
|
11263
|
-
|
|
11264
|
-
|
|
11325
|
+
import_fs15 = require("fs");
|
|
11326
|
+
import_path17 = require("path");
|
|
11327
|
+
init_skill_name();
|
|
11265
11328
|
CIRCUIT_BREAKER_THRESHOLD = 3;
|
|
11266
11329
|
NEGATIVE_SIGNALS = /* @__PURE__ */ new Set(["hallucination_caught", "disagreement", "unique_unconfirmed"]);
|
|
11267
11330
|
SIGNAL_EXPIRY_DAYS = 30;
|
|
@@ -11290,13 +11353,13 @@ var init_performance_reader = __esm({
|
|
|
11290
11353
|
cachedScores = null;
|
|
11291
11354
|
cachedMtimeMs = 0;
|
|
11292
11355
|
constructor(projectRoot) {
|
|
11293
|
-
this.filePath = (0,
|
|
11356
|
+
this.filePath = (0, import_path17.join)(projectRoot, ".gossip", "agent-performance.jsonl");
|
|
11294
11357
|
}
|
|
11295
11358
|
/** Read all signals and compute per-agent scores (cached by file mtime) */
|
|
11296
11359
|
getScores() {
|
|
11297
11360
|
let mtimeMs = 0;
|
|
11298
11361
|
try {
|
|
11299
|
-
mtimeMs = (0,
|
|
11362
|
+
mtimeMs = (0, import_fs15.statSync)(this.filePath).mtimeMs;
|
|
11300
11363
|
} catch {
|
|
11301
11364
|
}
|
|
11302
11365
|
if (this.cachedScores && mtimeMs === this.cachedMtimeMs) {
|
|
@@ -11345,9 +11408,10 @@ var init_performance_reader = __esm({
|
|
|
11345
11408
|
getCountersSince(agentId, category, sinceMs) {
|
|
11346
11409
|
const allSignals = this.readSignalsRaw();
|
|
11347
11410
|
const counters = { correct: 0, hallucinated: 0 };
|
|
11411
|
+
const normalizedTarget = normalizeSkillName(category);
|
|
11348
11412
|
for (const s of allSignals) {
|
|
11349
11413
|
if (s.agentId !== agentId) continue;
|
|
11350
|
-
if (s.category !==
|
|
11414
|
+
if (normalizeSkillName(s.category ?? "") !== normalizedTarget) continue;
|
|
11351
11415
|
const ts = s.timestamp ? new Date(s.timestamp).getTime() : 0;
|
|
11352
11416
|
if (!isFinite(ts) || ts === 0 || ts < sinceMs) continue;
|
|
11353
11417
|
switch (s.signal) {
|
|
@@ -11372,9 +11436,9 @@ var init_performance_reader = __esm({
|
|
|
11372
11436
|
* Don't unify these — see getCountersSince doc and consensus 9369ebfc-a3654b51 f1.
|
|
11373
11437
|
*/
|
|
11374
11438
|
readSignalsRaw() {
|
|
11375
|
-
if (!(0,
|
|
11439
|
+
if (!(0, import_fs15.existsSync)(this.filePath)) return [];
|
|
11376
11440
|
try {
|
|
11377
|
-
const lines = (0,
|
|
11441
|
+
const lines = (0, import_fs15.readFileSync)(this.filePath, "utf-8").trim().split("\n").filter(Boolean);
|
|
11378
11442
|
const all = lines.map((line) => {
|
|
11379
11443
|
try {
|
|
11380
11444
|
return JSON.parse(line);
|
|
@@ -11407,10 +11471,10 @@ var init_performance_reader = __esm({
|
|
|
11407
11471
|
}
|
|
11408
11472
|
}
|
|
11409
11473
|
readSignals() {
|
|
11410
|
-
if (!(0,
|
|
11474
|
+
if (!(0, import_fs15.existsSync)(this.filePath)) return [];
|
|
11411
11475
|
try {
|
|
11412
11476
|
const expiryMs = Date.now() - SIGNAL_EXPIRY_DAYS * 864e5;
|
|
11413
|
-
const lines = (0,
|
|
11477
|
+
const lines = (0, import_fs15.readFileSync)(this.filePath, "utf-8").trim().split("\n").filter(Boolean);
|
|
11414
11478
|
const all = lines.map((line) => {
|
|
11415
11479
|
try {
|
|
11416
11480
|
return JSON.parse(line);
|
|
@@ -11622,6 +11686,7 @@ var init_performance_reader = __esm({
|
|
|
11622
11686
|
const timeFreshness = Math.pow(0.5, daysSinceLastSignal / halfLife);
|
|
11623
11687
|
reliability = 0.5 + (reliability - 0.5) * timeFreshness;
|
|
11624
11688
|
}
|
|
11689
|
+
const MIN_CATEGORY_N = 5;
|
|
11625
11690
|
const categoryAccuracy = {};
|
|
11626
11691
|
const allCategories = /* @__PURE__ */ new Set([
|
|
11627
11692
|
...Object.keys(a.categoryCorrect),
|
|
@@ -11630,7 +11695,7 @@ var init_performance_reader = __esm({
|
|
|
11630
11695
|
for (const cat of allCategories) {
|
|
11631
11696
|
const c = a.categoryCorrect[cat] ?? 0;
|
|
11632
11697
|
const h = a.categoryHallucinated[cat] ?? 0;
|
|
11633
|
-
if (c + h
|
|
11698
|
+
if (c + h >= MIN_CATEGORY_N) categoryAccuracy[cat] = c / (c + h);
|
|
11634
11699
|
}
|
|
11635
11700
|
const consec = consecutiveFailures.get(id) || 0;
|
|
11636
11701
|
scores.set(id, {
|
|
@@ -11675,11 +11740,11 @@ var init_performance_reader = __esm({
|
|
|
11675
11740
|
return result;
|
|
11676
11741
|
}
|
|
11677
11742
|
getImplScore(agentId) {
|
|
11678
|
-
if (!(0,
|
|
11743
|
+
if (!(0, import_fs15.existsSync)(this.filePath)) return null;
|
|
11679
11744
|
try {
|
|
11680
11745
|
const now = Date.now();
|
|
11681
11746
|
const expiryMs = now - SIGNAL_EXPIRY_DAYS * 864e5;
|
|
11682
|
-
const lines = (0,
|
|
11747
|
+
const lines = (0, import_fs15.readFileSync)(this.filePath, "utf-8").trim().split("\n").filter(Boolean);
|
|
11683
11748
|
let pass = 0, fail = 0, approved = 0, rejected = 0, lastImplSignalMs = 0;
|
|
11684
11749
|
for (const line of lines) {
|
|
11685
11750
|
try {
|
|
@@ -11718,12 +11783,12 @@ var init_performance_reader = __esm({
|
|
|
11718
11783
|
});
|
|
11719
11784
|
|
|
11720
11785
|
// packages/orchestrator/src/skill-counters.ts
|
|
11721
|
-
var
|
|
11786
|
+
var import_fs16, import_path18, STALE_THRESHOLD, PROMOTION_RATE, PROMOTION_MIN_WINDOW, SkillCounterTracker;
|
|
11722
11787
|
var init_skill_counters = __esm({
|
|
11723
11788
|
"packages/orchestrator/src/skill-counters.ts"() {
|
|
11724
11789
|
"use strict";
|
|
11725
|
-
|
|
11726
|
-
|
|
11790
|
+
import_fs16 = require("fs");
|
|
11791
|
+
import_path18 = require("path");
|
|
11727
11792
|
STALE_THRESHOLD = 30;
|
|
11728
11793
|
PROMOTION_RATE = 0.8;
|
|
11729
11794
|
PROMOTION_MIN_WINDOW = 20;
|
|
@@ -11732,7 +11797,7 @@ var init_skill_counters = __esm({
|
|
|
11732
11797
|
filePath;
|
|
11733
11798
|
dirty = false;
|
|
11734
11799
|
constructor(projectRoot) {
|
|
11735
|
-
this.filePath = (0,
|
|
11800
|
+
this.filePath = (0, import_path18.join)(projectRoot, ".gossip", "skill-counters.json");
|
|
11736
11801
|
this.load();
|
|
11737
11802
|
}
|
|
11738
11803
|
/**
|
|
@@ -11807,15 +11872,15 @@ var init_skill_counters = __esm({
|
|
|
11807
11872
|
/** Flush counters to disk. Call during gossip_collect. */
|
|
11808
11873
|
flush() {
|
|
11809
11874
|
if (!this.dirty) return;
|
|
11810
|
-
const dir = (0,
|
|
11811
|
-
if (!(0,
|
|
11812
|
-
(0,
|
|
11875
|
+
const dir = (0, import_path18.dirname)(this.filePath);
|
|
11876
|
+
if (!(0, import_fs16.existsSync)(dir)) (0, import_fs16.mkdirSync)(dir, { recursive: true });
|
|
11877
|
+
(0, import_fs16.writeFileSync)(this.filePath, JSON.stringify(this.data, null, 2) + "\n");
|
|
11813
11878
|
this.dirty = false;
|
|
11814
11879
|
}
|
|
11815
11880
|
load() {
|
|
11816
11881
|
try {
|
|
11817
|
-
if ((0,
|
|
11818
|
-
const raw = JSON.parse((0,
|
|
11882
|
+
if ((0, import_fs16.existsSync)(this.filePath)) {
|
|
11883
|
+
const raw = JSON.parse((0, import_fs16.readFileSync)(this.filePath, "utf-8"));
|
|
11819
11884
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
11820
11885
|
for (const [agentId, skills] of Object.entries(raw)) {
|
|
11821
11886
|
if (!skills || typeof skills !== "object") {
|
|
@@ -11842,14 +11907,14 @@ function shortConsensusId() {
|
|
|
11842
11907
|
const hex3 = (0, import_crypto6.randomUUID)().replace(/-/g, "");
|
|
11843
11908
|
return hex3.slice(0, 8) + "-" + hex3.slice(8, 16);
|
|
11844
11909
|
}
|
|
11845
|
-
var import_promises3,
|
|
11910
|
+
var import_promises3, import_fs17, import_crypto6, import_path19, SUMMARY_HEADER, FALLBACK_MAX_LENGTH, MAX_SUMMARY_LENGTH, MAX_CROSS_REVIEW_ENTRIES, VALID_ACTIONS, ANCHOR_PATTERN, ConsensusEngine;
|
|
11846
11911
|
var init_consensus_engine = __esm({
|
|
11847
11912
|
"packages/orchestrator/src/consensus-engine.ts"() {
|
|
11848
11913
|
"use strict";
|
|
11849
11914
|
import_promises3 = require("fs/promises");
|
|
11850
|
-
|
|
11915
|
+
import_fs17 = require("fs");
|
|
11851
11916
|
import_crypto6 = require("crypto");
|
|
11852
|
-
|
|
11917
|
+
import_path19 = require("path");
|
|
11853
11918
|
SUMMARY_HEADER = "## Consensus Summary";
|
|
11854
11919
|
FALLBACK_MAX_LENGTH = 2e3;
|
|
11855
11920
|
MAX_SUMMARY_LENGTH = 5e3;
|
|
@@ -11885,7 +11950,7 @@ var init_consensus_engine = __esm({
|
|
|
11885
11950
|
for (const r of results) {
|
|
11886
11951
|
const wt = r.worktreeInfo?.path;
|
|
11887
11952
|
if (wt && typeof wt === "string") {
|
|
11888
|
-
next.add((0,
|
|
11953
|
+
next.add((0, import_path19.resolve)(wt));
|
|
11889
11954
|
}
|
|
11890
11955
|
}
|
|
11891
11956
|
let changed = next.size !== this.currentWorktreeRoots.size;
|
|
@@ -12713,9 +12778,9 @@ ${safeSnippet}
|
|
|
12713
12778
|
*/
|
|
12714
12779
|
/** Guard: resolved path must stay inside one of the valid roots */
|
|
12715
12780
|
isInsideAnyRoot(candidate, roots) {
|
|
12716
|
-
const normalized = (0,
|
|
12781
|
+
const normalized = (0, import_path19.resolve)(candidate);
|
|
12717
12782
|
return roots.some((root) => {
|
|
12718
|
-
const normalizedRoot = (0,
|
|
12783
|
+
const normalizedRoot = (0, import_path19.resolve)(root);
|
|
12719
12784
|
return normalized === normalizedRoot || normalized.startsWith(normalizedRoot + "/");
|
|
12720
12785
|
});
|
|
12721
12786
|
}
|
|
@@ -12725,7 +12790,7 @@ ${safeSnippet}
|
|
|
12725
12790
|
const fileName = fileRef.split("/").pop();
|
|
12726
12791
|
for (const root of roots) {
|
|
12727
12792
|
try {
|
|
12728
|
-
const candidate = (0,
|
|
12793
|
+
const candidate = (0, import_path19.join)(root, fileRef);
|
|
12729
12794
|
if (this.isInsideAnyRoot(candidate, roots)) {
|
|
12730
12795
|
await (0, import_promises3.stat)(candidate);
|
|
12731
12796
|
return candidate;
|
|
@@ -12734,7 +12799,7 @@ ${safeSnippet}
|
|
|
12734
12799
|
}
|
|
12735
12800
|
if (fileName !== fileRef) {
|
|
12736
12801
|
try {
|
|
12737
|
-
const candidate = (0,
|
|
12802
|
+
const candidate = (0, import_path19.join)(root, fileName);
|
|
12738
12803
|
if (this.isInsideAnyRoot(candidate, roots)) {
|
|
12739
12804
|
await (0, import_promises3.stat)(candidate);
|
|
12740
12805
|
return candidate;
|
|
@@ -12744,7 +12809,7 @@ ${safeSnippet}
|
|
|
12744
12809
|
}
|
|
12745
12810
|
const searchDirs = ["packages", "src", "apps", "tests", "test", "tools", "scripts", "lib"];
|
|
12746
12811
|
for (const dir of searchDirs) {
|
|
12747
|
-
const found = await this.findFile((0,
|
|
12812
|
+
const found = await this.findFile((0, import_path19.join)(root, dir), fileName, roots);
|
|
12748
12813
|
if (found) return found;
|
|
12749
12814
|
}
|
|
12750
12815
|
}
|
|
@@ -12754,7 +12819,7 @@ ${safeSnippet}
|
|
|
12754
12819
|
try {
|
|
12755
12820
|
const entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
|
|
12756
12821
|
for (const entry of entries) {
|
|
12757
|
-
const fullPath = (0,
|
|
12822
|
+
const fullPath = (0, import_path19.join)(dir, entry.name);
|
|
12758
12823
|
if (entry.isFile() && entry.name === fileName) {
|
|
12759
12824
|
if (!this.isInsideAnyRoot(fullPath, validRoots)) return null;
|
|
12760
12825
|
return fullPath;
|
|
@@ -12780,7 +12845,7 @@ ${safeSnippet}
|
|
|
12780
12845
|
const sourceExts = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
12781
12846
|
for (const root of roots) {
|
|
12782
12847
|
for (const dir of searchDirs) {
|
|
12783
|
-
const result = await this.grepDir((0,
|
|
12848
|
+
const result = await this.grepDir((0, import_path19.join)(root, dir), identifier, sourceExts, CONTEXT_LINES);
|
|
12784
12849
|
if (result) return result;
|
|
12785
12850
|
}
|
|
12786
12851
|
}
|
|
@@ -12791,7 +12856,7 @@ ${safeSnippet}
|
|
|
12791
12856
|
try {
|
|
12792
12857
|
const entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
|
|
12793
12858
|
for (const entry of entries) {
|
|
12794
|
-
const fullPath = (0,
|
|
12859
|
+
const fullPath = (0, import_path19.join)(dir, entry.name);
|
|
12795
12860
|
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git" && entry.name !== "dist") {
|
|
12796
12861
|
const found = await this.grepDir(fullPath, identifier, exts, contextLines);
|
|
12797
12862
|
if (found) return found;
|
|
@@ -13285,11 +13350,11 @@ ${safeSnippet}
|
|
|
13285
13350
|
dumpFailedCrossReview(reviewerAgentId, text) {
|
|
13286
13351
|
if (!this.config.projectRoot) return;
|
|
13287
13352
|
try {
|
|
13288
|
-
const dir = (0,
|
|
13289
|
-
(0,
|
|
13353
|
+
const dir = (0, import_path19.join)(this.config.projectRoot, ".gossip", "cross-review-failures");
|
|
13354
|
+
(0, import_fs17.mkdirSync)(dir, { recursive: true });
|
|
13290
13355
|
const safeId2 = reviewerAgentId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
13291
13356
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13292
|
-
(0,
|
|
13357
|
+
(0, import_fs17.writeFileSync)((0, import_path19.join)(dir, `${safeId2}-${ts}.txt`), text);
|
|
13293
13358
|
} catch {
|
|
13294
13359
|
}
|
|
13295
13360
|
}
|
|
@@ -13362,12 +13427,12 @@ function validateSignal(signal) {
|
|
|
13362
13427
|
throw new Error(`Signal validation failed: unknown type "${signal.type}"`);
|
|
13363
13428
|
}
|
|
13364
13429
|
}
|
|
13365
|
-
var
|
|
13430
|
+
var import_fs18, import_path20, VALID_CONSENSUS_SIGNALS, VALID_IMPL_SIGNALS, VALID_META_SIGNALS, PerformanceWriter;
|
|
13366
13431
|
var init_performance_writer = __esm({
|
|
13367
13432
|
"packages/orchestrator/src/performance-writer.ts"() {
|
|
13368
13433
|
"use strict";
|
|
13369
|
-
|
|
13370
|
-
|
|
13434
|
+
import_fs18 = require("fs");
|
|
13435
|
+
import_path20 = require("path");
|
|
13371
13436
|
VALID_CONSENSUS_SIGNALS = /* @__PURE__ */ new Set([
|
|
13372
13437
|
"agreement",
|
|
13373
13438
|
"disagreement",
|
|
@@ -13393,19 +13458,19 @@ var init_performance_writer = __esm({
|
|
|
13393
13458
|
PerformanceWriter = class {
|
|
13394
13459
|
filePath;
|
|
13395
13460
|
constructor(projectRoot) {
|
|
13396
|
-
const dir = (0,
|
|
13397
|
-
if (!(0,
|
|
13398
|
-
this.filePath = (0,
|
|
13461
|
+
const dir = (0, import_path20.join)(projectRoot, ".gossip");
|
|
13462
|
+
if (!(0, import_fs18.existsSync)(dir)) (0, import_fs18.mkdirSync)(dir, { recursive: true });
|
|
13463
|
+
this.filePath = (0, import_path20.join)(dir, "agent-performance.jsonl");
|
|
13399
13464
|
}
|
|
13400
13465
|
appendSignal(signal) {
|
|
13401
13466
|
validateSignal(signal);
|
|
13402
|
-
(0,
|
|
13467
|
+
(0, import_fs18.appendFileSync)(this.filePath, JSON.stringify(signal) + "\n");
|
|
13403
13468
|
}
|
|
13404
13469
|
appendSignals(signals) {
|
|
13405
13470
|
if (signals.length === 0) return;
|
|
13406
13471
|
for (const s of signals) validateSignal(s);
|
|
13407
13472
|
const data = signals.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
13408
|
-
(0,
|
|
13473
|
+
(0, import_fs18.appendFileSync)(this.filePath, data);
|
|
13409
13474
|
}
|
|
13410
13475
|
};
|
|
13411
13476
|
}
|
|
@@ -13604,12 +13669,12 @@ ${topFindings}`;
|
|
|
13604
13669
|
});
|
|
13605
13670
|
|
|
13606
13671
|
// packages/orchestrator/src/session-context.ts
|
|
13607
|
-
var
|
|
13672
|
+
var import_fs19, import_path21, log3, SessionContext;
|
|
13608
13673
|
var init_session_context = __esm({
|
|
13609
13674
|
"packages/orchestrator/src/session-context.ts"() {
|
|
13610
13675
|
"use strict";
|
|
13611
|
-
|
|
13612
|
-
|
|
13676
|
+
import_fs19 = require("fs");
|
|
13677
|
+
import_path21 = require("path");
|
|
13613
13678
|
log3 = (msg) => process.stderr.write(`[gossipcat] ${msg}
|
|
13614
13679
|
`);
|
|
13615
13680
|
SessionContext = class _SessionContext {
|
|
@@ -13623,7 +13688,7 @@ var init_session_context = __esm({
|
|
|
13623
13688
|
this.projectRoot = config2.projectRoot;
|
|
13624
13689
|
this.llm = config2.llm;
|
|
13625
13690
|
try {
|
|
13626
|
-
const gossipPath = (0,
|
|
13691
|
+
const gossipPath = (0, import_path21.join)(config2.projectRoot, ".gossip", "agents", "_project", "memory", "session-gossip.jsonl");
|
|
13627
13692
|
const { existsSync: ex, readFileSync: rf } = require("fs");
|
|
13628
13693
|
if (ex(gossipPath)) {
|
|
13629
13694
|
const lines = rf(gossipPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
@@ -13675,9 +13740,9 @@ var init_session_context = __esm({
|
|
|
13675
13740
|
this.sessionGossip.shift();
|
|
13676
13741
|
}
|
|
13677
13742
|
try {
|
|
13678
|
-
const gossipPath = (0,
|
|
13679
|
-
(0,
|
|
13680
|
-
(0,
|
|
13743
|
+
const gossipPath = (0, import_path21.join)(this.projectRoot, ".gossip", "agents", "_project", "memory", "session-gossip.jsonl");
|
|
13744
|
+
(0, import_fs19.mkdirSync)((0, import_path21.dirname)(gossipPath), { recursive: true });
|
|
13745
|
+
(0, import_fs19.appendFileSync)(gossipPath, JSON.stringify({ agentId, taskSummary: summary, timestamp: Date.now() }) + "\n");
|
|
13681
13746
|
this.rotateJsonlFile(gossipPath, 100, 50);
|
|
13682
13747
|
} catch {
|
|
13683
13748
|
}
|
|
@@ -13698,10 +13763,10 @@ ${result.slice(0, 2e3)}` }
|
|
|
13698
13763
|
/** Rotate a JSONL file: if over maxEntries lines, keep only the last keepEntries. */
|
|
13699
13764
|
rotateJsonlFile(filePath, maxEntries, keepEntries) {
|
|
13700
13765
|
try {
|
|
13701
|
-
const content = (0,
|
|
13766
|
+
const content = (0, import_fs19.readFileSync)(filePath, "utf-8");
|
|
13702
13767
|
const lines = content.trim().split("\n").filter((l) => l.length > 0);
|
|
13703
13768
|
if (lines.length > maxEntries) {
|
|
13704
|
-
(0,
|
|
13769
|
+
(0, import_fs19.writeFileSync)(filePath, lines.slice(-keepEntries).join("\n") + "\n");
|
|
13705
13770
|
}
|
|
13706
13771
|
} catch {
|
|
13707
13772
|
}
|
|
@@ -13720,13 +13785,13 @@ function shouldSkipConsensus(task, agents, costMode, agreementHistory) {
|
|
|
13720
13785
|
const firstWord = task.trim().split(/\s+/)[0] || "";
|
|
13721
13786
|
return OBSERVATION_VERBS.test(firstWord);
|
|
13722
13787
|
}
|
|
13723
|
-
var import_crypto8,
|
|
13788
|
+
var import_crypto8, import_fs20, import_path22, log4, DispatchPipeline, SECURITY_KEYWORDS, OBSERVATION_VERBS;
|
|
13724
13789
|
var init_dispatch_pipeline = __esm({
|
|
13725
13790
|
"packages/orchestrator/src/dispatch-pipeline.ts"() {
|
|
13726
13791
|
"use strict";
|
|
13727
13792
|
import_crypto8 = require("crypto");
|
|
13728
|
-
|
|
13729
|
-
|
|
13793
|
+
import_fs20 = require("fs");
|
|
13794
|
+
import_path22 = require("path");
|
|
13730
13795
|
init_types2();
|
|
13731
13796
|
init_skill_loader();
|
|
13732
13797
|
init_prompt_assembler();
|
|
@@ -13815,8 +13880,8 @@ var init_dispatch_pipeline = __esm({
|
|
|
13815
13880
|
this.sessionContext = new SessionContext({ llm: config2.llm ?? null, projectRoot: config2.projectRoot });
|
|
13816
13881
|
this.worktreeManager.pruneOrphans().catch((err) => log4(`Orphan cleanup failed: ${err.message}`));
|
|
13817
13882
|
try {
|
|
13818
|
-
const projectMemDir = (0,
|
|
13819
|
-
(0,
|
|
13883
|
+
const projectMemDir = (0, import_path22.join)(config2.projectRoot, ".gossip", "agents", "_project", "memory");
|
|
13884
|
+
(0, import_fs20.mkdirSync)(projectMemDir, { recursive: true });
|
|
13820
13885
|
} catch {
|
|
13821
13886
|
}
|
|
13822
13887
|
}
|
|
@@ -13887,11 +13952,11 @@ var init_dispatch_pipeline = __esm({
|
|
|
13887
13952
|
const specRefs = extractSpecReferences(task);
|
|
13888
13953
|
if (specRefs.length > 0) {
|
|
13889
13954
|
try {
|
|
13890
|
-
const specPath = (0,
|
|
13891
|
-
const realSpecPath = (0,
|
|
13892
|
-
const realRoot = (0,
|
|
13955
|
+
const specPath = (0, import_path22.resolve)(this.projectRoot, specRefs[0]);
|
|
13956
|
+
const realSpecPath = (0, import_fs20.realpathSync)(specPath);
|
|
13957
|
+
const realRoot = (0, import_fs20.realpathSync)(this.projectRoot);
|
|
13893
13958
|
if (realSpecPath.startsWith(realRoot + "/")) {
|
|
13894
|
-
const specContent = (0,
|
|
13959
|
+
const specContent = (0, import_fs20.readFileSync)(realSpecPath, "utf-8");
|
|
13895
13960
|
const implFiles = extractSpecReferences(task, specContent);
|
|
13896
13961
|
const enrichment = buildSpecReviewEnrichment(implFiles);
|
|
13897
13962
|
if (enrichment) specReviewContext = enrichment;
|
|
@@ -13899,7 +13964,7 @@ var init_dispatch_pipeline = __esm({
|
|
|
13899
13964
|
} catch {
|
|
13900
13965
|
}
|
|
13901
13966
|
}
|
|
13902
|
-
const memoryDir = (0,
|
|
13967
|
+
const memoryDir = (0, import_path22.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "knowledge");
|
|
13903
13968
|
const promptContent = assemblePrompt({
|
|
13904
13969
|
memory: memory || void 0,
|
|
13905
13970
|
memoryDir,
|
|
@@ -13959,7 +14024,7 @@ var init_dispatch_pipeline = __esm({
|
|
|
13959
14024
|
const elapsedMs = (entry.completedAt ?? Date.now()) - entry.startedAt;
|
|
13960
14025
|
process.stderr.write(`[gossipcat] \u2705 relay \u2190 ${entry.agentId} [${entry.id}] OK (${(elapsedMs / 1e3).toFixed(1)}s, ${(event.payload.result || "").length} chars)
|
|
13961
14026
|
`);
|
|
13962
|
-
(0,
|
|
14027
|
+
(0, import_fs20.appendFileSync)((0, import_path22.join)(this.projectRoot, ".gossip", "task-graph.jsonl"), JSON.stringify({
|
|
13963
14028
|
type: "task.completed",
|
|
13964
14029
|
taskId: entry.id,
|
|
13965
14030
|
agentId: entry.agentId,
|
|
@@ -13983,7 +14048,7 @@ var init_dispatch_pipeline = __esm({
|
|
|
13983
14048
|
const elapsedMs = (entry.completedAt ?? Date.now()) - entry.startedAt;
|
|
13984
14049
|
process.stderr.write(`[gossipcat] \u274C relay \u2190 ${entry.agentId} [${entry.id}] FAILED (${(elapsedMs / 1e3).toFixed(1)}s) \u2014 ${event.payload.error}
|
|
13985
14050
|
`);
|
|
13986
|
-
(0,
|
|
14051
|
+
(0, import_fs20.appendFileSync)((0, import_path22.join)(this.projectRoot, ".gossip", "task-graph.jsonl"), JSON.stringify({
|
|
13987
14052
|
type: "task.failed",
|
|
13988
14053
|
taskId: entry.id,
|
|
13989
14054
|
agentId: entry.agentId,
|
|
@@ -14910,13 +14975,13 @@ function parseYamlLikeToolCall(content) {
|
|
|
14910
14975
|
const args = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
14911
14976
|
return { tool, args };
|
|
14912
14977
|
}
|
|
14913
|
-
var
|
|
14978
|
+
var import_fs21, import_path23, log5, AGENT_ID_RE, TAG_PATTERN, BLOCK_RE, BLOCK_IN_FENCE_RE, ToolRouter, ToolExecutor;
|
|
14914
14979
|
var init_tool_router = __esm({
|
|
14915
14980
|
"packages/orchestrator/src/tool-router.ts"() {
|
|
14916
14981
|
"use strict";
|
|
14917
14982
|
init_tool_definitions();
|
|
14918
|
-
|
|
14919
|
-
|
|
14983
|
+
import_fs21 = require("fs");
|
|
14984
|
+
import_path23 = require("path");
|
|
14920
14985
|
log5 = (msg) => process.stderr.write(`[tool-router] ${msg}
|
|
14921
14986
|
`);
|
|
14922
14987
|
AGENT_ID_RE = /^[a-zA-Z0-9_-]+$/;
|
|
@@ -15322,12 +15387,12 @@ ${agentOutputs}` }
|
|
|
15322
15387
|
const fsp = await import("fs/promises");
|
|
15323
15388
|
const updatedIds = [];
|
|
15324
15389
|
for (const id of pending.agentIds) {
|
|
15325
|
-
const agentDir = (0,
|
|
15326
|
-
const filePath = (0,
|
|
15327
|
-
if (!(0,
|
|
15390
|
+
const agentDir = (0, import_path23.join)(this.projectRoot, ".gossip", "agents", id);
|
|
15391
|
+
const filePath = (0, import_path23.join)(agentDir, "instructions.md");
|
|
15392
|
+
if (!(0, import_fs21.existsSync)(agentDir)) {
|
|
15328
15393
|
await fsp.mkdir(agentDir, { recursive: true });
|
|
15329
15394
|
}
|
|
15330
|
-
if ((0,
|
|
15395
|
+
if ((0, import_fs21.existsSync)(filePath)) {
|
|
15331
15396
|
await fsp.appendFile(filePath, `
|
|
15332
15397
|
|
|
15333
15398
|
${pending.instruction}`, "utf-8");
|
|
@@ -15441,9 +15506,9 @@ ${collectResult.consensus.summary}`;
|
|
|
15441
15506
|
async handlePlan(args) {
|
|
15442
15507
|
let task = String(args.task);
|
|
15443
15508
|
try {
|
|
15444
|
-
const specPath = (0,
|
|
15445
|
-
if ((0,
|
|
15446
|
-
const spec = (0,
|
|
15509
|
+
const specPath = (0, import_path23.join)(this.projectRoot, ".gossip", "spec.md");
|
|
15510
|
+
if ((0, import_fs21.existsSync)(specPath)) {
|
|
15511
|
+
const spec = (0, import_fs21.readFileSync)(specPath, "utf-8");
|
|
15447
15512
|
task = `${task}
|
|
15448
15513
|
|
|
15449
15514
|
[Project Spec]
|
|
@@ -15501,11 +15566,11 @@ ${taskLines.join("\n")}`,
|
|
|
15501
15566
|
if (!this.llm) {
|
|
15502
15567
|
return { text: "Tool error: LLM not available for spec generation" };
|
|
15503
15568
|
}
|
|
15504
|
-
const specPath = (0,
|
|
15569
|
+
const specPath = (0, import_path23.join)(this.projectRoot, ".gossip", "spec.md");
|
|
15505
15570
|
let existingSpec = "";
|
|
15506
15571
|
try {
|
|
15507
|
-
if ((0,
|
|
15508
|
-
existingSpec = (0,
|
|
15572
|
+
if ((0, import_fs21.existsSync)(specPath)) {
|
|
15573
|
+
existingSpec = (0, import_fs21.readFileSync)(specPath, "utf-8");
|
|
15509
15574
|
}
|
|
15510
15575
|
} catch {
|
|
15511
15576
|
}
|
|
@@ -15540,8 +15605,8 @@ Keep it SHORT \u2014 under 30 lines. This is a working document, not a design do
|
|
|
15540
15605
|
]);
|
|
15541
15606
|
const specContent = response.text || "";
|
|
15542
15607
|
try {
|
|
15543
|
-
const { mkdirSync:
|
|
15544
|
-
|
|
15608
|
+
const { mkdirSync: mkdirSync22, writeFileSync: writeFS } = require("fs");
|
|
15609
|
+
mkdirSync22((0, import_path23.join)(this.projectRoot, ".gossip"), { recursive: true });
|
|
15545
15610
|
writeFS(specPath, specContent, "utf-8");
|
|
15546
15611
|
} catch (err) {
|
|
15547
15612
|
return { text: `Spec generated but failed to save: ${err.message}
|
|
@@ -15579,11 +15644,11 @@ ${lines.join("\n")}` };
|
|
|
15579
15644
|
if (!this.registry.get(agentId)) {
|
|
15580
15645
|
return { text: `Tool error: agent "${agentId}" not found in registry` };
|
|
15581
15646
|
}
|
|
15582
|
-
const tasksPath = (0,
|
|
15583
|
-
if (!(0,
|
|
15647
|
+
const tasksPath = (0, import_path23.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "tasks.jsonl");
|
|
15648
|
+
if (!(0, import_fs21.existsSync)(tasksPath)) {
|
|
15584
15649
|
return { text: `Agent "${agentId}" \u2014 no task history found.` };
|
|
15585
15650
|
}
|
|
15586
|
-
const rawLines = (0,
|
|
15651
|
+
const rawLines = (0, import_fs21.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
15587
15652
|
const last5 = rawLines.slice(-5).map((line) => {
|
|
15588
15653
|
try {
|
|
15589
15654
|
return JSON.parse(line);
|
|
@@ -15600,11 +15665,11 @@ Last ${last5.length} tasks:
|
|
|
15600
15665
|
${formatted.join("\n")}` };
|
|
15601
15666
|
}
|
|
15602
15667
|
handleAgentPerformance() {
|
|
15603
|
-
const perfPath = (0,
|
|
15604
|
-
if (!(0,
|
|
15668
|
+
const perfPath = (0, import_path23.join)(this.projectRoot, ".gossip", "agent-performance.jsonl");
|
|
15669
|
+
if (!(0, import_fs21.existsSync)(perfPath)) {
|
|
15605
15670
|
return { text: "No performance data found." };
|
|
15606
15671
|
}
|
|
15607
|
-
const rawLines = (0,
|
|
15672
|
+
const rawLines = (0, import_fs21.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
15608
15673
|
const last20 = rawLines.slice(-20).map((line) => {
|
|
15609
15674
|
try {
|
|
15610
15675
|
return JSON.parse(line);
|
|
@@ -15655,11 +15720,11 @@ Instruction: "${instruction}"`,
|
|
|
15655
15720
|
if (!this.registry.get(agentId)) {
|
|
15656
15721
|
return { text: `Tool error: agent "${agentId}" not found in registry` };
|
|
15657
15722
|
}
|
|
15658
|
-
const tasksPath = (0,
|
|
15659
|
-
if (!(0,
|
|
15723
|
+
const tasksPath = (0, import_path23.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "tasks.jsonl");
|
|
15724
|
+
if (!(0, import_fs21.existsSync)(tasksPath)) {
|
|
15660
15725
|
return { text: `No task history for agent "${agentId}".` };
|
|
15661
15726
|
}
|
|
15662
|
-
const rawLines = (0,
|
|
15727
|
+
const rawLines = (0, import_fs21.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
15663
15728
|
const entries = rawLines.slice(-limit).map((line) => {
|
|
15664
15729
|
try {
|
|
15665
15730
|
return JSON.parse(line);
|
|
@@ -15719,17 +15784,34 @@ ${formatted.join("\n")}` };
|
|
|
15719
15784
|
});
|
|
15720
15785
|
|
|
15721
15786
|
// packages/orchestrator/src/archetype-catalog.ts
|
|
15722
|
-
|
|
15787
|
+
function findArchetypeCatalog() {
|
|
15788
|
+
const candidates = [
|
|
15789
|
+
(0, import_path24.resolve)(__dirname, "data", "archetypes.json"),
|
|
15790
|
+
// bundled prod: dist-mcp/data/archetypes.json
|
|
15791
|
+
(0, import_path24.resolve)(__dirname, "..", "..", "..", "data", "archetypes.json"),
|
|
15792
|
+
// dev ts-node: packages/orchestrator/src → repo/data
|
|
15793
|
+
(0, import_path24.resolve)(process.cwd(), "data", "archetypes.json")
|
|
15794
|
+
// last-resort dev fallback
|
|
15795
|
+
];
|
|
15796
|
+
for (const p of candidates) {
|
|
15797
|
+
if ((0, import_fs22.existsSync)(p)) return p;
|
|
15798
|
+
}
|
|
15799
|
+
return null;
|
|
15800
|
+
}
|
|
15801
|
+
var import_fs22, import_path24, ArchetypeCatalog;
|
|
15723
15802
|
var init_archetype_catalog = __esm({
|
|
15724
15803
|
"packages/orchestrator/src/archetype-catalog.ts"() {
|
|
15725
15804
|
"use strict";
|
|
15726
|
-
|
|
15727
|
-
|
|
15728
|
-
DEFAULT_PATH = (0, import_path23.resolve)(__dirname, "..", "..", "..", "data", "archetypes.json");
|
|
15805
|
+
import_fs22 = require("fs");
|
|
15806
|
+
import_path24 = require("path");
|
|
15729
15807
|
ArchetypeCatalog = class {
|
|
15730
15808
|
archetypes;
|
|
15731
15809
|
constructor(catalogPath) {
|
|
15732
|
-
const
|
|
15810
|
+
const path2 = catalogPath ?? findArchetypeCatalog();
|
|
15811
|
+
if (!path2) {
|
|
15812
|
+
throw new Error("archetypes.json not found \u2014 tried bundled dist-mcp/data/, dev ts-node path, and cwd/data/. Reinstall gossipcat.");
|
|
15813
|
+
}
|
|
15814
|
+
const raw = (0, import_fs22.readFileSync)(path2, "utf-8");
|
|
15733
15815
|
this.archetypes = JSON.parse(raw);
|
|
15734
15816
|
}
|
|
15735
15817
|
/** Return all archetype IDs */
|
|
@@ -15779,13 +15861,13 @@ var init_archetype_catalog = __esm({
|
|
|
15779
15861
|
});
|
|
15780
15862
|
|
|
15781
15863
|
// packages/orchestrator/src/project-initializer.ts
|
|
15782
|
-
var
|
|
15864
|
+
var import_fs23, import_path25, SIGNAL_DIRS, SIGNAL_FILES, LANG_FILES, ProjectInitializer;
|
|
15783
15865
|
var init_project_initializer = __esm({
|
|
15784
15866
|
"packages/orchestrator/src/project-initializer.ts"() {
|
|
15785
15867
|
"use strict";
|
|
15786
15868
|
init_archetype_catalog();
|
|
15787
|
-
|
|
15788
|
-
|
|
15869
|
+
import_fs23 = require("fs");
|
|
15870
|
+
import_path25 = require("path");
|
|
15789
15871
|
SIGNAL_DIRS = [
|
|
15790
15872
|
"src",
|
|
15791
15873
|
"pages",
|
|
@@ -15817,16 +15899,16 @@ var init_project_initializer = __esm({
|
|
|
15817
15899
|
this.config = config2;
|
|
15818
15900
|
}
|
|
15819
15901
|
scanDirectory(root) {
|
|
15820
|
-
const absRoot = (0,
|
|
15902
|
+
const absRoot = (0, import_path25.resolve)(root);
|
|
15821
15903
|
const signals = { dependencies: [], directories: [], files: [] };
|
|
15822
15904
|
for (const [file2, lang] of Object.entries(LANG_FILES)) {
|
|
15823
|
-
if (this.safeExists(absRoot, (0,
|
|
15905
|
+
if (this.safeExists(absRoot, (0, import_path25.join)(absRoot, file2))) signals.language = lang;
|
|
15824
15906
|
}
|
|
15825
|
-
const pkgPath = (0,
|
|
15907
|
+
const pkgPath = (0, import_path25.join)(absRoot, "package.json");
|
|
15826
15908
|
if (this.safeExists(absRoot, pkgPath)) {
|
|
15827
15909
|
signals.files.push("package.json");
|
|
15828
15910
|
try {
|
|
15829
|
-
const pkg = JSON.parse((0,
|
|
15911
|
+
const pkg = JSON.parse((0, import_fs23.readFileSync)(pkgPath, "utf-8"));
|
|
15830
15912
|
signals.dependencies = [
|
|
15831
15913
|
...Object.keys(pkg.dependencies || {}),
|
|
15832
15914
|
...Object.keys(pkg.devDependencies || {})
|
|
@@ -15836,18 +15918,18 @@ var init_project_initializer = __esm({
|
|
|
15836
15918
|
if (!signals.language) signals.language = "JavaScript";
|
|
15837
15919
|
}
|
|
15838
15920
|
for (const dir of SIGNAL_DIRS) {
|
|
15839
|
-
const p = (0,
|
|
15921
|
+
const p = (0, import_path25.join)(absRoot, dir);
|
|
15840
15922
|
if (this.safeExists(absRoot, p) && this.isDir(p)) signals.directories.push(`${dir}/`);
|
|
15841
15923
|
}
|
|
15842
|
-
const wfPath = (0,
|
|
15924
|
+
const wfPath = (0, import_path25.join)(absRoot, ".github", "workflows");
|
|
15843
15925
|
if (this.safeExists(absRoot, wfPath) && this.isDir(wfPath)) {
|
|
15844
15926
|
signals.directories.push(".github/workflows/");
|
|
15845
15927
|
}
|
|
15846
15928
|
for (const file2 of SIGNAL_FILES) {
|
|
15847
|
-
if (this.safeExists(absRoot, (0,
|
|
15929
|
+
if (this.safeExists(absRoot, (0, import_path25.join)(absRoot, file2))) signals.files.push(file2);
|
|
15848
15930
|
}
|
|
15849
15931
|
for (const file2 of Object.keys(LANG_FILES)) {
|
|
15850
|
-
if (this.safeExists(absRoot, (0,
|
|
15932
|
+
if (this.safeExists(absRoot, (0, import_path25.join)(absRoot, file2))) signals.files.push(file2);
|
|
15851
15933
|
}
|
|
15852
15934
|
return signals;
|
|
15853
15935
|
}
|
|
@@ -15865,7 +15947,8 @@ var init_project_initializer = __esm({
|
|
|
15865
15947
|
for (const p of ["google", "anthropic", "openai"]) {
|
|
15866
15948
|
if (await this.config.keyProvider(p)) providers.push(p);
|
|
15867
15949
|
}
|
|
15868
|
-
|
|
15950
|
+
const hostIsClaudeCode = process.env.CLAUDECODE === "1" || !!process.env.CLAUDE_CODE_ENTRYPOINT;
|
|
15951
|
+
if (!providers.length && !hostIsClaudeCode) {
|
|
15869
15952
|
return { text: "No API keys available. Run gossipcat setup to configure providers." };
|
|
15870
15953
|
}
|
|
15871
15954
|
const catalog = new ArchetypeCatalog(this.config.catalogPath);
|
|
@@ -15877,11 +15960,14 @@ var init_project_initializer = __esm({
|
|
|
15877
15960
|
anthropic: { best: "claude-opus-4-6", fast: "claude-sonnet-4-6", cheapest: "claude-haiku-4-5" },
|
|
15878
15961
|
openai: { best: "gpt-4o", fast: "gpt-4o", cheapest: "gpt-4o-mini" }
|
|
15879
15962
|
};
|
|
15880
|
-
const availableModels =
|
|
15881
|
-
|
|
15882
|
-
|
|
15883
|
-
|
|
15884
|
-
|
|
15963
|
+
const availableModels = [
|
|
15964
|
+
...hostIsClaudeCode ? ["none: none (native Claude Code orchestration \u2014 no API key needed, preferred for main_agent on this host)"] : [],
|
|
15965
|
+
...providers.map((p) => {
|
|
15966
|
+
const tiers = MODEL_TIERS[p];
|
|
15967
|
+
if (!tiers) return `${p}: (use any available model)`;
|
|
15968
|
+
return `${p}: ${tiers.best} (best), ${tiers.fast} (fast), ${tiers.cheapest} (cheapest)`;
|
|
15969
|
+
})
|
|
15970
|
+
].join("\n");
|
|
15885
15971
|
const brainstormCtx = signals.brainstormContext;
|
|
15886
15972
|
const systemPrompt = `You are configuring an agent team for a software project.
|
|
15887
15973
|
|
|
@@ -15928,7 +16014,7 @@ You may add additional skills from the table above based on project needs. Do NO
|
|
|
15928
16014
|
- Pick the best archetype and customize roles for this specific project
|
|
15929
16015
|
- Use ONLY the exact model names listed above
|
|
15930
16016
|
- Choose models based on project complexity: simple \u2192 "fast" for all, complex \u2192 "best" for critical roles
|
|
15931
|
-
- For the main_agent (orchestrator), use the "best" model from the primary provider
|
|
16017
|
+
- For the main_agent (orchestrator): ${hostIsClaudeCode ? 'PREFER { "provider": "none", "model": "none" } \u2014 native Claude Code orchestration needs no API key and is the zero-config default on this host. Only pick a keyed provider if the user explicitly asks for one.' : 'use the "best" model from the primary provider'}
|
|
15932
16018
|
- Do NOT include agent IDs \u2014 the system generates them automatically
|
|
15933
16019
|
- **Scale team size to project complexity.** Simple (single-page app, script, simple game) \u2192 1-2 agents. Medium \u2192 2-3. Complex multi-module \u2192 4-5. NEVER duplicate roles.
|
|
15934
16020
|
- Max 5 agents, prefer fewer. Every agent costs money.
|
|
@@ -15979,13 +16065,13 @@ ${agentList}`,
|
|
|
15979
16065
|
}
|
|
15980
16066
|
async writeConfig(projectRoot) {
|
|
15981
16067
|
if (!this.pendingProposal) throw new Error("No pending proposal to write");
|
|
15982
|
-
const gossipDir2 = (0,
|
|
15983
|
-
if (!(0,
|
|
16068
|
+
const gossipDir2 = (0, import_path25.join)(projectRoot, ".gossip");
|
|
16069
|
+
if (!(0, import_fs23.existsSync)(gossipDir2)) (0, import_fs23.mkdirSync)(gossipDir2, { recursive: true });
|
|
15984
16070
|
const agents = {};
|
|
15985
16071
|
for (const a of this.pendingProposal.agents || []) {
|
|
15986
16072
|
agents[a.id] = { provider: a.provider, model: a.model, preset: a.preset, skills: a.skills || [] };
|
|
15987
|
-
const agentDir = (0,
|
|
15988
|
-
if (!(0,
|
|
16073
|
+
const agentDir = (0, import_path25.join)(gossipDir2, "agents", a.id);
|
|
16074
|
+
if (!(0, import_fs23.existsSync)(agentDir)) (0, import_fs23.mkdirSync)(agentDir, { recursive: true });
|
|
15989
16075
|
}
|
|
15990
16076
|
const config2 = {
|
|
15991
16077
|
main_agent: this.pendingProposal.main_agent,
|
|
@@ -15996,20 +16082,20 @@ ${agentList}`,
|
|
|
15996
16082
|
},
|
|
15997
16083
|
agents
|
|
15998
16084
|
};
|
|
15999
|
-
(0,
|
|
16085
|
+
(0, import_fs23.writeFileSync)((0, import_path25.join)(gossipDir2, "config.json"), JSON.stringify(config2, null, 2));
|
|
16000
16086
|
}
|
|
16001
16087
|
safeExists(root, target) {
|
|
16002
|
-
const resolved = (0,
|
|
16088
|
+
const resolved = (0, import_path25.resolve)(target);
|
|
16003
16089
|
if (!resolved.startsWith(root)) return false;
|
|
16004
16090
|
try {
|
|
16005
|
-
return !(0,
|
|
16091
|
+
return !(0, import_fs23.lstatSync)(resolved).isSymbolicLink();
|
|
16006
16092
|
} catch {
|
|
16007
16093
|
return false;
|
|
16008
16094
|
}
|
|
16009
16095
|
}
|
|
16010
16096
|
isDir(target) {
|
|
16011
16097
|
try {
|
|
16012
|
-
return (0,
|
|
16098
|
+
return (0, import_fs23.lstatSync)(target).isDirectory();
|
|
16013
16099
|
} catch {
|
|
16014
16100
|
return false;
|
|
16015
16101
|
}
|
|
@@ -16019,12 +16105,12 @@ ${agentList}`,
|
|
|
16019
16105
|
});
|
|
16020
16106
|
|
|
16021
16107
|
// packages/orchestrator/src/team-manager.ts
|
|
16022
|
-
var
|
|
16108
|
+
var import_fs24, import_path26, TeamManager;
|
|
16023
16109
|
var init_team_manager = __esm({
|
|
16024
16110
|
"packages/orchestrator/src/team-manager.ts"() {
|
|
16025
16111
|
"use strict";
|
|
16026
|
-
|
|
16027
|
-
|
|
16112
|
+
import_fs24 = require("fs");
|
|
16113
|
+
import_path26 = require("path");
|
|
16028
16114
|
TeamManager = class {
|
|
16029
16115
|
registry;
|
|
16030
16116
|
pipeline;
|
|
@@ -16080,8 +16166,8 @@ var init_team_manager = __esm({
|
|
|
16080
16166
|
applyAdd(config2) {
|
|
16081
16167
|
this.registry.register(config2);
|
|
16082
16168
|
this.writeConfig();
|
|
16083
|
-
const dir = (0,
|
|
16084
|
-
if (!(0,
|
|
16169
|
+
const dir = (0, import_path26.join)(this.projectRoot, ".gossip", "agents", config2.id);
|
|
16170
|
+
if (!(0, import_fs24.existsSync)(dir)) (0, import_fs24.mkdirSync)(dir, { recursive: true });
|
|
16085
16171
|
this.pendingAction = null;
|
|
16086
16172
|
}
|
|
16087
16173
|
applyRemove(agentId) {
|
|
@@ -16115,11 +16201,11 @@ var init_team_manager = __esm({
|
|
|
16115
16201
|
};
|
|
16116
16202
|
}
|
|
16117
16203
|
writeConfig() {
|
|
16118
|
-
const configPath = (0,
|
|
16204
|
+
const configPath = (0, import_path26.join)(this.projectRoot, ".gossip", "config.json");
|
|
16119
16205
|
let existing = {};
|
|
16120
|
-
if ((0,
|
|
16206
|
+
if ((0, import_fs24.existsSync)(configPath)) {
|
|
16121
16207
|
try {
|
|
16122
|
-
existing = JSON.parse((0,
|
|
16208
|
+
existing = JSON.parse((0, import_fs24.readFileSync)(configPath, "utf-8"));
|
|
16123
16209
|
} catch {
|
|
16124
16210
|
}
|
|
16125
16211
|
}
|
|
@@ -16131,9 +16217,9 @@ var init_team_manager = __esm({
|
|
|
16131
16217
|
...a.preset ? { preset: a.preset } : {},
|
|
16132
16218
|
skills: a.skills
|
|
16133
16219
|
}));
|
|
16134
|
-
const dir = (0,
|
|
16135
|
-
if (!(0,
|
|
16136
|
-
(0,
|
|
16220
|
+
const dir = (0, import_path26.join)(this.projectRoot, ".gossip");
|
|
16221
|
+
if (!(0, import_fs24.existsSync)(dir)) (0, import_fs24.mkdirSync)(dir, { recursive: true });
|
|
16222
|
+
(0, import_fs24.writeFileSync)(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
16137
16223
|
}
|
|
16138
16224
|
};
|
|
16139
16225
|
}
|
|
@@ -16355,8 +16441,8 @@ message: Your question?
|
|
|
16355
16441
|
}
|
|
16356
16442
|
/** Start all worker agents (connect to relay) */
|
|
16357
16443
|
async start() {
|
|
16358
|
-
const { existsSync:
|
|
16359
|
-
const { join:
|
|
16444
|
+
const { existsSync: existsSync44, readFileSync: readFileSync41 } = await import("fs");
|
|
16445
|
+
const { join: join49 } = await import("path");
|
|
16360
16446
|
for (const config2 of this.registry.getAll()) {
|
|
16361
16447
|
if (config2.native) continue;
|
|
16362
16448
|
if (this.workers.has(config2.id)) continue;
|
|
@@ -16365,8 +16451,8 @@ message: Your question?
|
|
|
16365
16451
|
apiKey = await this.keyProviderFn(config2.provider) ?? void 0;
|
|
16366
16452
|
}
|
|
16367
16453
|
const llm = createProvider(config2.provider, config2.model, apiKey);
|
|
16368
|
-
const instructionsPath =
|
|
16369
|
-
const instructions =
|
|
16454
|
+
const instructionsPath = join49(this.projectRoot, ".gossip", "agents", config2.id, "instructions.md");
|
|
16455
|
+
const instructions = existsSync44(instructionsPath) ? readFileSync41(instructionsPath, "utf-8") : void 0;
|
|
16370
16456
|
const enableWebSearch = config2.preset === "researcher" || config2.skills.includes("research");
|
|
16371
16457
|
const worker = new WorkerAgent(config2.id, llm, this.relayUrl, ALL_TOOLS, instructions, enableWebSearch, this.relayApiKey);
|
|
16372
16458
|
await worker.start();
|
|
@@ -16548,16 +16634,16 @@ message: Your question?
|
|
|
16548
16634
|
this.registry.register(config2);
|
|
16549
16635
|
}
|
|
16550
16636
|
async syncWorkers(keyProvider) {
|
|
16551
|
-
const { existsSync:
|
|
16552
|
-
const { join:
|
|
16637
|
+
const { existsSync: existsSync44, readFileSync: readFileSync41 } = await import("fs");
|
|
16638
|
+
const { join: join49 } = await import("path");
|
|
16553
16639
|
let added = 0;
|
|
16554
16640
|
for (const ac of this.registry.getAll()) {
|
|
16555
16641
|
if (ac.native) continue;
|
|
16556
16642
|
if (this.workers.has(ac.id)) continue;
|
|
16557
16643
|
const key = await keyProvider(ac.provider);
|
|
16558
16644
|
const llm = createProvider(ac.provider, ac.model, key ?? void 0, void 0, ac.base_url);
|
|
16559
|
-
const instructionsPath =
|
|
16560
|
-
const instructions =
|
|
16645
|
+
const instructionsPath = join49(this.projectRoot, ".gossip", "agents", ac.id, "instructions.md");
|
|
16646
|
+
const instructions = existsSync44(instructionsPath) ? readFileSync41(instructionsPath, "utf-8") : void 0;
|
|
16561
16647
|
const enableWebSearch = ac.preset === "researcher" || ac.skills.includes("research");
|
|
16562
16648
|
const worker = new WorkerAgent(ac.id, llm, this.relayUrl, ALL_TOOLS, instructions, enableWebSearch, this.relayApiKey);
|
|
16563
16649
|
await worker.start();
|
|
@@ -17078,12 +17164,12 @@ var init_consensus_types = __esm({
|
|
|
17078
17164
|
});
|
|
17079
17165
|
|
|
17080
17166
|
// packages/orchestrator/src/skill-index.ts
|
|
17081
|
-
var
|
|
17167
|
+
var import_fs25, import_path27, DANGEROUS_KEYS, SkillIndex;
|
|
17082
17168
|
var init_skill_index = __esm({
|
|
17083
17169
|
"packages/orchestrator/src/skill-index.ts"() {
|
|
17084
17170
|
"use strict";
|
|
17085
|
-
|
|
17086
|
-
|
|
17171
|
+
import_fs25 = require("fs");
|
|
17172
|
+
import_path27 = require("path");
|
|
17087
17173
|
init_skill_name();
|
|
17088
17174
|
DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype", "_project"]);
|
|
17089
17175
|
SkillIndex = class {
|
|
@@ -17092,7 +17178,7 @@ var init_skill_index = __esm({
|
|
|
17092
17178
|
dirty = false;
|
|
17093
17179
|
_exists = false;
|
|
17094
17180
|
constructor(projectRoot) {
|
|
17095
|
-
this.filePath = (0,
|
|
17181
|
+
this.filePath = (0, import_path27.join)(projectRoot, ".gossip", "skill-index.json");
|
|
17096
17182
|
this.load();
|
|
17097
17183
|
}
|
|
17098
17184
|
/** Bind a skill to an agent (creates or updates the slot) */
|
|
@@ -17284,7 +17370,7 @@ var init_skill_index = __esm({
|
|
|
17284
17370
|
}
|
|
17285
17371
|
load() {
|
|
17286
17372
|
try {
|
|
17287
|
-
const raw = (0,
|
|
17373
|
+
const raw = (0, import_fs25.readFileSync)(this.filePath, "utf-8");
|
|
17288
17374
|
const parsed = JSON.parse(raw);
|
|
17289
17375
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
17290
17376
|
for (const key of Object.keys(parsed)) {
|
|
@@ -17306,9 +17392,9 @@ var init_skill_index = __esm({
|
|
|
17306
17392
|
}
|
|
17307
17393
|
save() {
|
|
17308
17394
|
if (!this.dirty) return;
|
|
17309
|
-
const dir = (0,
|
|
17310
|
-
(0,
|
|
17311
|
-
(0,
|
|
17395
|
+
const dir = (0, import_path27.dirname)(this.filePath);
|
|
17396
|
+
(0, import_fs25.mkdirSync)(dir, { recursive: true });
|
|
17397
|
+
(0, import_fs25.writeFileSync)(this.filePath, JSON.stringify(this.data, null, 2) + "\n");
|
|
17312
17398
|
this._exists = true;
|
|
17313
17399
|
this.dirty = false;
|
|
17314
17400
|
}
|
|
@@ -17323,12 +17409,12 @@ function safeId(value) {
|
|
|
17323
17409
|
}
|
|
17324
17410
|
return encodeURIComponent(value);
|
|
17325
17411
|
}
|
|
17326
|
-
var
|
|
17412
|
+
var import_fs26, import_path28, TaskGraphSync;
|
|
17327
17413
|
var init_task_graph_sync = __esm({
|
|
17328
17414
|
"packages/orchestrator/src/task-graph-sync.ts"() {
|
|
17329
17415
|
"use strict";
|
|
17330
|
-
|
|
17331
|
-
|
|
17416
|
+
import_fs26 = require("fs");
|
|
17417
|
+
import_path28 = require("path");
|
|
17332
17418
|
TaskGraphSync = class {
|
|
17333
17419
|
constructor(graph, supabaseUrl, supabaseKey, userId, projectId, projectRoot, displayName, migration) {
|
|
17334
17420
|
this.graph = graph;
|
|
@@ -17338,7 +17424,7 @@ var init_task_graph_sync = __esm({
|
|
|
17338
17424
|
this.projectId = projectId;
|
|
17339
17425
|
this.displayName = displayName;
|
|
17340
17426
|
this.migration = migration;
|
|
17341
|
-
this.gossipDir = (0,
|
|
17427
|
+
this.gossipDir = (0, import_path28.join)(projectRoot, ".gossip");
|
|
17342
17428
|
}
|
|
17343
17429
|
gossipDir;
|
|
17344
17430
|
migrationDone = false;
|
|
@@ -17461,9 +17547,9 @@ var init_task_graph_sync = __esm({
|
|
|
17461
17547
|
});
|
|
17462
17548
|
}
|
|
17463
17549
|
async syncAgentScores() {
|
|
17464
|
-
const perfPath = (0,
|
|
17465
|
-
if (!(0,
|
|
17466
|
-
const content = (0,
|
|
17550
|
+
const perfPath = (0, import_path28.join)(this.gossipDir, "agent-performance.jsonl");
|
|
17551
|
+
if (!(0, import_fs26.existsSync)(perfPath)) return 0;
|
|
17552
|
+
const content = (0, import_fs26.readFileSync)(perfPath, "utf-8");
|
|
17467
17553
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
17468
17554
|
const meta3 = this.graph.getSyncMeta();
|
|
17469
17555
|
let synced = 0;
|
|
@@ -17610,61 +17696,61 @@ Return JSON: { "<agentId>": "<summary>", ... }`
|
|
|
17610
17696
|
// packages/orchestrator/src/rules-loader.ts
|
|
17611
17697
|
function findBundledRules() {
|
|
17612
17698
|
const candidates = [
|
|
17613
|
-
(0,
|
|
17614
|
-
(0,
|
|
17615
|
-
(0,
|
|
17699
|
+
(0, import_path29.resolve)(__dirname, "default-rules", "gossipcat-rules.md"),
|
|
17700
|
+
(0, import_path29.resolve)(__dirname, "..", "default-rules", "gossipcat-rules.md"),
|
|
17701
|
+
(0, import_path29.resolve)(process.cwd(), "packages", "orchestrator", "src", "default-rules", "gossipcat-rules.md")
|
|
17616
17702
|
];
|
|
17617
17703
|
for (const p of candidates) {
|
|
17618
|
-
if ((0,
|
|
17704
|
+
if ((0, import_fs27.existsSync)(p)) return p;
|
|
17619
17705
|
}
|
|
17620
17706
|
return null;
|
|
17621
17707
|
}
|
|
17622
17708
|
function ensureRulesFile(projectRoot) {
|
|
17623
|
-
const target = (0,
|
|
17624
|
-
if ((0,
|
|
17709
|
+
const target = (0, import_path29.join)(projectRoot, ".gossip", "rules.md");
|
|
17710
|
+
if ((0, import_fs27.existsSync)(target)) return { created: false, path: target };
|
|
17625
17711
|
const bundled = findBundledRules();
|
|
17626
17712
|
if (!bundled) return { created: false, path: null };
|
|
17627
17713
|
try {
|
|
17628
|
-
(0,
|
|
17629
|
-
(0,
|
|
17714
|
+
(0, import_fs27.mkdirSync)((0, import_path29.dirname)(target), { recursive: true });
|
|
17715
|
+
(0, import_fs27.copyFileSync)(bundled, target);
|
|
17630
17716
|
return { created: true, path: target };
|
|
17631
17717
|
} catch {
|
|
17632
17718
|
return { created: false, path: null };
|
|
17633
17719
|
}
|
|
17634
17720
|
}
|
|
17635
17721
|
function readRulesContent(projectRoot) {
|
|
17636
|
-
const local = (0,
|
|
17637
|
-
if ((0,
|
|
17722
|
+
const local = (0, import_path29.join)(projectRoot, ".gossip", "rules.md");
|
|
17723
|
+
if ((0, import_fs27.existsSync)(local)) {
|
|
17638
17724
|
try {
|
|
17639
|
-
return (0,
|
|
17725
|
+
return (0, import_fs27.readFileSync)(local, "utf-8");
|
|
17640
17726
|
} catch {
|
|
17641
17727
|
}
|
|
17642
17728
|
}
|
|
17643
17729
|
const bundled = findBundledRules();
|
|
17644
17730
|
if (bundled) {
|
|
17645
17731
|
try {
|
|
17646
|
-
return (0,
|
|
17732
|
+
return (0, import_fs27.readFileSync)(bundled, "utf-8");
|
|
17647
17733
|
} catch {
|
|
17648
17734
|
}
|
|
17649
17735
|
}
|
|
17650
17736
|
return null;
|
|
17651
17737
|
}
|
|
17652
|
-
var
|
|
17738
|
+
var import_fs27, import_path29;
|
|
17653
17739
|
var init_rules_loader = __esm({
|
|
17654
17740
|
"packages/orchestrator/src/rules-loader.ts"() {
|
|
17655
17741
|
"use strict";
|
|
17656
|
-
|
|
17657
|
-
|
|
17742
|
+
import_fs27 = require("fs");
|
|
17743
|
+
import_path29 = require("path");
|
|
17658
17744
|
}
|
|
17659
17745
|
});
|
|
17660
17746
|
|
|
17661
17747
|
// packages/orchestrator/src/bootstrap.ts
|
|
17662
|
-
var
|
|
17748
|
+
var import_fs28, import_path30, log6, BootstrapGenerator;
|
|
17663
17749
|
var init_bootstrap = __esm({
|
|
17664
17750
|
"packages/orchestrator/src/bootstrap.ts"() {
|
|
17665
17751
|
"use strict";
|
|
17666
|
-
|
|
17667
|
-
|
|
17752
|
+
import_fs28 = require("fs");
|
|
17753
|
+
import_path30 = require("path");
|
|
17668
17754
|
init_rules_loader();
|
|
17669
17755
|
log6 = (msg) => process.stderr.write(`[gossipcat] ${msg}
|
|
17670
17756
|
`);
|
|
@@ -17687,23 +17773,23 @@ var init_bootstrap = __esm({
|
|
|
17687
17773
|
};
|
|
17688
17774
|
}
|
|
17689
17775
|
migrateConfig() {
|
|
17690
|
-
const oldPath = (0,
|
|
17691
|
-
const newPath = (0,
|
|
17692
|
-
if (!(0,
|
|
17693
|
-
(0,
|
|
17694
|
-
(0,
|
|
17776
|
+
const oldPath = (0, import_path30.resolve)(this.projectRoot, "gossip.agents.json");
|
|
17777
|
+
const newPath = (0, import_path30.resolve)(this.projectRoot, ".gossip", "config.json");
|
|
17778
|
+
if (!(0, import_fs28.existsSync)(newPath) && (0, import_fs28.existsSync)(oldPath)) {
|
|
17779
|
+
(0, import_fs28.mkdirSync)((0, import_path30.resolve)(this.projectRoot, ".gossip"), { recursive: true });
|
|
17780
|
+
(0, import_fs28.copyFileSync)(oldPath, newPath);
|
|
17695
17781
|
log6("Migrated config to .gossip/config.json \u2014 gossip.agents.json is now ignored.");
|
|
17696
17782
|
}
|
|
17697
17783
|
}
|
|
17698
17784
|
loadConfig() {
|
|
17699
17785
|
const paths = [
|
|
17700
|
-
(0,
|
|
17701
|
-
(0,
|
|
17786
|
+
(0, import_path30.resolve)(this.projectRoot, ".gossip", "config.json"),
|
|
17787
|
+
(0, import_path30.resolve)(this.projectRoot, "gossip.agents.json")
|
|
17702
17788
|
];
|
|
17703
17789
|
for (const p of paths) {
|
|
17704
|
-
if ((0,
|
|
17790
|
+
if ((0, import_fs28.existsSync)(p)) {
|
|
17705
17791
|
try {
|
|
17706
|
-
return JSON.parse((0,
|
|
17792
|
+
return JSON.parse((0, import_fs28.readFileSync)(p, "utf-8"));
|
|
17707
17793
|
} catch {
|
|
17708
17794
|
log6("Config parse error, falling back to setup mode");
|
|
17709
17795
|
return null;
|
|
@@ -17724,9 +17810,9 @@ var init_bootstrap = __esm({
|
|
|
17724
17810
|
skills: ac.skills || [],
|
|
17725
17811
|
taskCount: 0
|
|
17726
17812
|
};
|
|
17727
|
-
const tasksPath = (0,
|
|
17728
|
-
if ((0,
|
|
17729
|
-
const lines = (0,
|
|
17813
|
+
const tasksPath = (0, import_path30.join)(this.projectRoot, ".gossip", "agents", id, "memory", "tasks.jsonl");
|
|
17814
|
+
if ((0, import_fs28.existsSync)(tasksPath)) {
|
|
17815
|
+
const lines = (0, import_fs28.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
17730
17816
|
let count = 0;
|
|
17731
17817
|
let lastTs = "";
|
|
17732
17818
|
for (const line of lines) {
|
|
@@ -17740,9 +17826,9 @@ var init_bootstrap = __esm({
|
|
|
17740
17826
|
summary.taskCount = count;
|
|
17741
17827
|
if (lastTs) summary.lastActive = lastTs.split("T")[0];
|
|
17742
17828
|
}
|
|
17743
|
-
const memPath = (0,
|
|
17744
|
-
if ((0,
|
|
17745
|
-
const content = (0,
|
|
17829
|
+
const memPath = (0, import_path30.join)(this.projectRoot, ".gossip", "agents", id, "memory", "MEMORY.md");
|
|
17830
|
+
if ((0, import_fs28.existsSync)(memPath)) {
|
|
17831
|
+
const content = (0, import_fs28.readFileSync)(memPath, "utf-8").slice(0, 500);
|
|
17746
17832
|
const knowledgeLines = content.match(/- \[([^\]]+)\]/g);
|
|
17747
17833
|
if (knowledgeLines?.length) {
|
|
17748
17834
|
summary.topics = knowledgeLines.map((l) => l.replace(/- \[([^\]]+)\].*/, "$1")).join(", ");
|
|
@@ -17975,13 +18061,13 @@ Skills are auto-injected from agent config. Project-wide skills in .gossip/skill
|
|
|
17975
18061
|
* Returns the body content of the top knowledge files, capped at 2500 chars.
|
|
17976
18062
|
*/
|
|
17977
18063
|
readProjectMemory() {
|
|
17978
|
-
const knowledgeDir = (0,
|
|
17979
|
-
if (!(0,
|
|
17980
|
-
const files = (0,
|
|
18064
|
+
const knowledgeDir = (0, import_path30.join)(this.projectRoot, ".gossip", "agents", "_project", "memory", "knowledge");
|
|
18065
|
+
if (!(0, import_fs28.existsSync)(knowledgeDir)) return null;
|
|
18066
|
+
const files = (0, import_fs28.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md"));
|
|
17981
18067
|
if (files.length === 0) return null;
|
|
17982
18068
|
const scored = files.map((f) => {
|
|
17983
18069
|
try {
|
|
17984
|
-
const content = (0,
|
|
18070
|
+
const content = (0, import_fs28.readFileSync)((0, import_path30.join)(knowledgeDir, f), "utf-8");
|
|
17985
18071
|
const importance = parseFloat(content.match(/importance:\s*([\d.]+)/)?.[1] ?? "0.5");
|
|
17986
18072
|
const isPinned = /pinned:\s*true/i.test(content);
|
|
17987
18073
|
const tsPart = f.slice(0, 19);
|
|
@@ -18006,9 +18092,9 @@ Skills are auto-injected from agent config. Project-wide skills in .gossip/skill
|
|
|
18006
18092
|
* Annotates TODO/remaining lines where the referenced tool actually exists.
|
|
18007
18093
|
*/
|
|
18008
18094
|
verifyToolClaims(content) {
|
|
18009
|
-
const mcpPath = (0,
|
|
18010
|
-
if (!(0,
|
|
18011
|
-
const rawSource = (0,
|
|
18095
|
+
const mcpPath = (0, import_path30.join)(this.projectRoot, "apps", "cli", "src", "mcp-server-sdk.ts");
|
|
18096
|
+
if (!(0, import_fs28.existsSync)(mcpPath)) return content;
|
|
18097
|
+
const rawSource = (0, import_fs28.readFileSync)(mcpPath, "utf-8");
|
|
18012
18098
|
const source = rawSource.replace(/\/\/.*|\/\*[\s\S]*?\*\//g, "");
|
|
18013
18099
|
const keywordRe = /TODO|remaining|deferred|needed|pending/i;
|
|
18014
18100
|
const toolRe = /gossip_\w+/;
|
|
@@ -18026,10 +18112,10 @@ Skills are auto-injected from agent config. Project-wide skills in .gossip/skill
|
|
|
18026
18112
|
}
|
|
18027
18113
|
/** Read .gossip/next-session.md if it exists — user/orchestrator notes for the next session */
|
|
18028
18114
|
readNextSessionNotes() {
|
|
18029
|
-
const notesPath = (0,
|
|
18030
|
-
if (!(0,
|
|
18115
|
+
const notesPath = (0, import_path30.join)(this.projectRoot, ".gossip", "next-session.md");
|
|
18116
|
+
if (!(0, import_fs28.existsSync)(notesPath)) return null;
|
|
18031
18117
|
try {
|
|
18032
|
-
const content = (0,
|
|
18118
|
+
const content = (0, import_fs28.readFileSync)(notesPath, "utf-8").trim();
|
|
18033
18119
|
if (content.length === 0) return null;
|
|
18034
18120
|
return this.verifyToolClaims(content.slice(0, 3e3));
|
|
18035
18121
|
} catch {
|
|
@@ -18243,13 +18329,13 @@ var init_check_effectiveness = __esm({
|
|
|
18243
18329
|
});
|
|
18244
18330
|
|
|
18245
18331
|
// packages/orchestrator/src/skill-engine.ts
|
|
18246
|
-
var
|
|
18332
|
+
var import_fs29, import_crypto9, import_path31, SAFE_NAME, KNOWN_CATEGORIES, CATEGORY_KEYWORDS, REQUIRED_SECTIONS, BUNDLED_TEMPLATE, SkillEngine;
|
|
18247
18333
|
var init_skill_engine = __esm({
|
|
18248
18334
|
"packages/orchestrator/src/skill-engine.ts"() {
|
|
18249
18335
|
"use strict";
|
|
18250
|
-
|
|
18336
|
+
import_fs29 = require("fs");
|
|
18251
18337
|
import_crypto9 = require("crypto");
|
|
18252
|
-
|
|
18338
|
+
import_path31 = require("path");
|
|
18253
18339
|
init_skill_name();
|
|
18254
18340
|
init_check_effectiveness();
|
|
18255
18341
|
SAFE_NAME = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
@@ -18329,7 +18415,13 @@ NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST.
|
|
|
18329
18415
|
this.projectRoot = projectRoot;
|
|
18330
18416
|
}
|
|
18331
18417
|
techStackCache = void 0;
|
|
18332
|
-
|
|
18418
|
+
/**
|
|
18419
|
+
* Build the LLM prompt strings and metadata needed to generate a skill file.
|
|
18420
|
+
* Separated from generate() so callers (e.g. the native-utility re-entry path)
|
|
18421
|
+
* can obtain the prompt, dispatch it externally, and later call saveFromRaw()
|
|
18422
|
+
* with the result — without re-running the expensive data-gathering step.
|
|
18423
|
+
*/
|
|
18424
|
+
async buildPrompt(agentId, category) {
|
|
18333
18425
|
if (!SAFE_NAME.test(agentId)) {
|
|
18334
18426
|
throw new Error(`Invalid agent_id: "${agentId}". Must be lowercase alphanumeric with hyphens/underscores.`);
|
|
18335
18427
|
}
|
|
@@ -18350,9 +18442,9 @@ NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST.
|
|
|
18350
18442
|
}
|
|
18351
18443
|
}
|
|
18352
18444
|
let projectContext = "";
|
|
18353
|
-
const bootstrapPath = (0,
|
|
18354
|
-
if ((0,
|
|
18355
|
-
projectContext = (0,
|
|
18445
|
+
const bootstrapPath = (0, import_path31.join)(this.projectRoot, ".gossip", "bootstrap.md");
|
|
18446
|
+
if ((0, import_fs29.existsSync)(bootstrapPath)) {
|
|
18447
|
+
projectContext = (0, import_fs29.readFileSync)(bootstrapPath, "utf-8").slice(0, 1500);
|
|
18356
18448
|
}
|
|
18357
18449
|
if (this.techStackCache === void 0) {
|
|
18358
18450
|
this.techStackCache = await this.detectTechStack();
|
|
@@ -18368,10 +18460,13 @@ ${techStack}
|
|
|
18368
18460
|
const totalDispatches = agentScoreData?.totalSignals ?? 0;
|
|
18369
18461
|
const categoryConfirmations = findings.filter((f) => f.agentId === agentId).length;
|
|
18370
18462
|
const baselineRate = totalDispatches > 0 ? categoryConfirmations / totalDispatches : 0;
|
|
18371
|
-
const
|
|
18372
|
-
|
|
18373
|
-
|
|
18374
|
-
|
|
18463
|
+
const lifetime = this.perfReader.getCountersSince(agentId, category, 0);
|
|
18464
|
+
const baseline_accuracy_correct = lifetime.correct;
|
|
18465
|
+
const baseline_accuracy_hallucinated = lifetime.hallucinated;
|
|
18466
|
+
const bound_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
18467
|
+
const skillName = normalizeSkillName(category);
|
|
18468
|
+
const skillPath = (0, import_path31.join)(this.projectRoot, ".gossip", "agents", agentId, "skills", `${skillName}.md`);
|
|
18469
|
+
const system = `You are a senior prompt engineer who builds skill files for AI code review agents. Your skills are injected into agent system prompts at dispatch time \u2014 every word costs tokens and shapes behavior. You write concise, opinionated methodology that changes how an agent thinks about a specific class of problems.
|
|
18375
18470
|
|
|
18376
18471
|
Your output quality is measured by:
|
|
18377
18472
|
1. **Relevance** \u2014 every check must apply to THIS project's tech stack. Generic checklists are waste.
|
|
@@ -18383,11 +18478,8 @@ Study this reference skill \u2014 it represents the quality bar:
|
|
|
18383
18478
|
|
|
18384
18479
|
<reference_skill>
|
|
18385
18480
|
${template}
|
|
18386
|
-
</reference_skill
|
|
18387
|
-
|
|
18388
|
-
{
|
|
18389
|
-
role: "user",
|
|
18390
|
-
content: `Generate a skill file for agent "${agentId}" to improve its "${category}" review performance.
|
|
18481
|
+
</reference_skill>`;
|
|
18482
|
+
const user = `Generate a skill file for agent "${agentId}" to improve its "${category}" review performance.
|
|
18391
18483
|
|
|
18392
18484
|
<project_context>
|
|
18393
18485
|
${projectContext || "No project context available."}
|
|
@@ -18418,27 +18510,42 @@ Requirements:
|
|
|
18418
18510
|
- Keep under 150 lines
|
|
18419
18511
|
- CRITICAL: Tailor ALL content to the project's actual tech stack (see <tech_stack>). Only include checks relevant to technologies the project uses. If the project has no SQL database, do NOT mention SQL injection. If no HTML rendering, do NOT mention XSS. Generic security checklists waste agent prompt tokens.
|
|
18420
18512
|
- Reference actual project file paths and patterns from findings and context
|
|
18421
|
-
- Use bullet lists instead of markdown tables for Anti-Patterns (tables render poorly in agent prompts)
|
|
18422
|
-
|
|
18423
|
-
|
|
18424
|
-
|
|
18425
|
-
|
|
18426
|
-
|
|
18513
|
+
- Use bullet lists instead of markdown tables for Anti-Patterns (tables render poorly in agent prompts)`;
|
|
18514
|
+
return { system, user, skillName, skillPath, baseline_accuracy_correct, baseline_accuracy_hallucinated, bound_at };
|
|
18515
|
+
}
|
|
18516
|
+
/**
|
|
18517
|
+
* Persist a raw LLM-generated skill markdown string to disk.
|
|
18518
|
+
* Mirrors the post-LLM steps from generate(): code-fence stripping,
|
|
18519
|
+
* validation, snapshot-field injection, and atomic file write.
|
|
18520
|
+
*
|
|
18521
|
+
* Called by the native-utility re-entry path after gossip_relay delivers
|
|
18522
|
+
* the agent output back to the orchestrator.
|
|
18523
|
+
*/
|
|
18524
|
+
saveFromRaw(_agentId, _category, rawMarkdown, meta3) {
|
|
18525
|
+
let cleaned = rawMarkdown.trim();
|
|
18427
18526
|
if (cleaned.startsWith("```")) {
|
|
18428
18527
|
cleaned = cleaned.replace(/^```\w*\n/, "").replace(/\n```\s*$/, "").trim();
|
|
18429
18528
|
}
|
|
18430
18529
|
this.validateSkillContent(cleaned);
|
|
18431
|
-
|
|
18432
|
-
|
|
18433
|
-
|
|
18434
|
-
|
|
18435
|
-
|
|
18436
|
-
const
|
|
18437
|
-
|
|
18438
|
-
(0,
|
|
18439
|
-
|
|
18440
|
-
|
|
18441
|
-
|
|
18530
|
+
cleaned = this.injectSnapshotFields(cleaned, {
|
|
18531
|
+
baseline_accuracy_correct: meta3.baseline_accuracy_correct,
|
|
18532
|
+
baseline_accuracy_hallucinated: meta3.baseline_accuracy_hallucinated,
|
|
18533
|
+
bound_at: meta3.bound_at
|
|
18534
|
+
});
|
|
18535
|
+
const skillDir = (0, import_path31.join)(this.projectRoot, ".gossip", "agents", _agentId, "skills");
|
|
18536
|
+
(0, import_fs29.mkdirSync)(skillDir, { recursive: true });
|
|
18537
|
+
(0, import_fs29.writeFileSync)(meta3.skillPath, cleaned);
|
|
18538
|
+
return { path: meta3.skillPath, content: cleaned };
|
|
18539
|
+
}
|
|
18540
|
+
async generate(agentId, category) {
|
|
18541
|
+
const promptData = await this.buildPrompt(agentId, category);
|
|
18542
|
+
const messages = [
|
|
18543
|
+
{ role: "system", content: promptData.system },
|
|
18544
|
+
{ role: "user", content: promptData.user }
|
|
18545
|
+
];
|
|
18546
|
+
const response = await this.llm.generate(messages, { temperature: 0.3 });
|
|
18547
|
+
const content = response.text || "";
|
|
18548
|
+
return this.saveFromRaw(agentId, category, content, promptData);
|
|
18442
18549
|
}
|
|
18443
18550
|
/**
|
|
18444
18551
|
* Post-processes LLM-generated skill content to inject or overwrite snapshot fields
|
|
@@ -18481,24 +18588,24 @@ ${fm}
|
|
|
18481
18588
|
}
|
|
18482
18589
|
}
|
|
18483
18590
|
loadTemplate() {
|
|
18484
|
-
const userDir = (0,
|
|
18485
|
-
if ((0,
|
|
18486
|
-
const files = (0,
|
|
18591
|
+
const userDir = (0, import_path31.join)(this.projectRoot, ".gossip", "skill-templates");
|
|
18592
|
+
if ((0, import_fs29.existsSync)(userDir)) {
|
|
18593
|
+
const files = (0, import_fs29.readdirSync)(userDir).filter((f) => f.endsWith(".md"));
|
|
18487
18594
|
if (files.length > 0) {
|
|
18488
|
-
return (0,
|
|
18595
|
+
return (0, import_fs29.readFileSync)((0, import_path31.join)(userDir, files[0]), "utf-8");
|
|
18489
18596
|
}
|
|
18490
18597
|
}
|
|
18491
18598
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
18492
|
-
const cacheBase = (0,
|
|
18493
|
-
if ((0,
|
|
18599
|
+
const cacheBase = (0, import_path31.join)(home, ".claude", "plugins", "cache", "claude-plugins-official", "superpowers");
|
|
18600
|
+
if ((0, import_fs29.existsSync)(cacheBase)) {
|
|
18494
18601
|
try {
|
|
18495
|
-
const versions = (0,
|
|
18602
|
+
const versions = (0, import_fs29.readdirSync)(cacheBase).sort().reverse();
|
|
18496
18603
|
for (const ver of versions) {
|
|
18497
|
-
const skillPath = (0,
|
|
18498
|
-
if ((0,
|
|
18499
|
-
const realPath = (0,
|
|
18500
|
-
if (realPath.startsWith((0,
|
|
18501
|
-
return (0,
|
|
18604
|
+
const skillPath = (0, import_path31.join)(cacheBase, ver, "skills", "systematic-debugging", "SKILL.md");
|
|
18605
|
+
if ((0, import_fs29.existsSync)(skillPath)) {
|
|
18606
|
+
const realPath = (0, import_fs29.realpathSync)(skillPath);
|
|
18607
|
+
if (realPath.startsWith((0, import_path31.resolve)(cacheBase))) {
|
|
18608
|
+
return (0, import_fs29.readFileSync)(realPath, "utf-8");
|
|
18502
18609
|
}
|
|
18503
18610
|
}
|
|
18504
18611
|
}
|
|
@@ -18514,20 +18621,20 @@ ${fm}
|
|
|
18514
18621
|
*/
|
|
18515
18622
|
async detectTechStack() {
|
|
18516
18623
|
const inputs = [];
|
|
18517
|
-
const pkgPaths = [(0,
|
|
18624
|
+
const pkgPaths = [(0, import_path31.join)(this.projectRoot, "package.json")];
|
|
18518
18625
|
try {
|
|
18519
|
-
const packagesDir = (0,
|
|
18520
|
-
if ((0,
|
|
18521
|
-
for (const dir of (0,
|
|
18522
|
-
const p = (0,
|
|
18523
|
-
if ((0,
|
|
18626
|
+
const packagesDir = (0, import_path31.join)(this.projectRoot, "packages");
|
|
18627
|
+
if ((0, import_fs29.existsSync)(packagesDir)) {
|
|
18628
|
+
for (const dir of (0, import_fs29.readdirSync)(packagesDir)) {
|
|
18629
|
+
const p = (0, import_path31.join)(packagesDir, dir, "package.json");
|
|
18630
|
+
if ((0, import_fs29.existsSync)(p)) pkgPaths.push(p);
|
|
18524
18631
|
}
|
|
18525
18632
|
}
|
|
18526
18633
|
} catch {
|
|
18527
18634
|
}
|
|
18528
18635
|
for (const p of pkgPaths.slice(0, 5)) {
|
|
18529
18636
|
try {
|
|
18530
|
-
const pkg = JSON.parse((0,
|
|
18637
|
+
const pkg = JSON.parse((0, import_fs29.readFileSync)(p, "utf-8"));
|
|
18531
18638
|
const deps = Object.keys({ ...pkg.dependencies, ...pkg.devDependencies });
|
|
18532
18639
|
if (deps.length > 0) {
|
|
18533
18640
|
inputs.push(`${p.replace(this.projectRoot + "/", "")}: ${deps.join(", ")}`);
|
|
@@ -18536,7 +18643,7 @@ ${fm}
|
|
|
18536
18643
|
}
|
|
18537
18644
|
}
|
|
18538
18645
|
try {
|
|
18539
|
-
const srcDirs = ["src", "packages", "apps", "lib"].filter((d) => (0,
|
|
18646
|
+
const srcDirs = ["src", "packages", "apps", "lib"].filter((d) => (0, import_fs29.existsSync)((0, import_path31.join)(this.projectRoot, d)));
|
|
18540
18647
|
inputs.push(`Source dirs: ${srcDirs.join(", ") || "root"}`);
|
|
18541
18648
|
} catch {
|
|
18542
18649
|
}
|
|
@@ -18563,10 +18670,10 @@ ${inputs.join("\n")}
|
|
|
18563
18670
|
}
|
|
18564
18671
|
}
|
|
18565
18672
|
loadCategoryFindings(category) {
|
|
18566
|
-
const filePath = (0,
|
|
18567
|
-
if (!(0,
|
|
18673
|
+
const filePath = (0, import_path31.join)(this.projectRoot, ".gossip", "agent-performance.jsonl");
|
|
18674
|
+
if (!(0, import_fs29.existsSync)(filePath)) return [];
|
|
18568
18675
|
try {
|
|
18569
|
-
return (0,
|
|
18676
|
+
return (0, import_fs29.readFileSync)(filePath, "utf-8").trim().split("\n").filter(Boolean).map((line) => {
|
|
18570
18677
|
try {
|
|
18571
18678
|
return JSON.parse(line);
|
|
18572
18679
|
} catch {
|
|
@@ -18596,10 +18703,10 @@ ${inputs.join("\n")}
|
|
|
18596
18703
|
return { status: "pending", shouldUpdate: false };
|
|
18597
18704
|
}
|
|
18598
18705
|
const skillPath = this.resolveSkillPath(agentId, category);
|
|
18599
|
-
if (!(0,
|
|
18706
|
+
if (!(0, import_fs29.existsSync)(skillPath)) {
|
|
18600
18707
|
return { status: "pending", shouldUpdate: false };
|
|
18601
18708
|
}
|
|
18602
|
-
const raw = (0,
|
|
18709
|
+
const raw = (0, import_fs29.readFileSync)(skillPath, "utf-8");
|
|
18603
18710
|
const { frontmatter: rawFrontmatter, body } = this.parseSkillFile(raw);
|
|
18604
18711
|
const nowMs = Date.now();
|
|
18605
18712
|
const { frontmatter, mutated } = this.migrateIfNeeded(
|
|
@@ -18677,7 +18784,7 @@ ${inputs.join("\n")}
|
|
|
18677
18784
|
*/
|
|
18678
18785
|
resolveSkillPath(agentId, category) {
|
|
18679
18786
|
const skillName = normalizeSkillName(category);
|
|
18680
|
-
return (0,
|
|
18787
|
+
return (0, import_path31.join)(this.projectRoot, ".gossip", "agents", agentId, "skills", `${skillName}.md`);
|
|
18681
18788
|
}
|
|
18682
18789
|
/**
|
|
18683
18790
|
* Splits a skill file into its frontmatter key-value map and the body text
|
|
@@ -18765,11 +18872,11 @@ ${fmLines.join("\n")}
|
|
|
18765
18872
|
---${body}`;
|
|
18766
18873
|
const tmpPath = `${skillPath}.tmp.${process.pid}.${(0, import_crypto9.randomBytes)(4).toString("hex")}`;
|
|
18767
18874
|
try {
|
|
18768
|
-
(0,
|
|
18769
|
-
(0,
|
|
18875
|
+
(0, import_fs29.writeFileSync)(tmpPath, content, "utf-8");
|
|
18876
|
+
(0, import_fs29.renameSync)(tmpPath, skillPath);
|
|
18770
18877
|
} catch (err) {
|
|
18771
18878
|
try {
|
|
18772
|
-
(0,
|
|
18879
|
+
(0, import_fs29.unlinkSync)(tmpPath);
|
|
18773
18880
|
} catch {
|
|
18774
18881
|
}
|
|
18775
18882
|
throw err;
|
|
@@ -18780,12 +18887,12 @@ ${fmLines.join("\n")}
|
|
|
18780
18887
|
});
|
|
18781
18888
|
|
|
18782
18889
|
// packages/orchestrator/src/memory-searcher.ts
|
|
18783
|
-
var
|
|
18890
|
+
var import_fs30, import_path32, MAX_QUERY_LENGTH, MAX_KEYWORDS, MAX_TASK_FILE_BYTES, MemorySearcher;
|
|
18784
18891
|
var init_memory_searcher = __esm({
|
|
18785
18892
|
"packages/orchestrator/src/memory-searcher.ts"() {
|
|
18786
18893
|
"use strict";
|
|
18787
|
-
|
|
18788
|
-
|
|
18894
|
+
import_fs30 = require("fs");
|
|
18895
|
+
import_path32 = require("path");
|
|
18789
18896
|
MAX_QUERY_LENGTH = 500;
|
|
18790
18897
|
MAX_KEYWORDS = 20;
|
|
18791
18898
|
MAX_TASK_FILE_BYTES = 2 * 1024 * 1024;
|
|
@@ -18800,19 +18907,19 @@ var init_memory_searcher = __esm({
|
|
|
18800
18907
|
const limit = Math.min(maxResults, 10);
|
|
18801
18908
|
const keywords = this.extractKeywords(safeQuery);
|
|
18802
18909
|
if (keywords.length === 0) return [];
|
|
18803
|
-
const memDir = (0,
|
|
18804
|
-
if (!(0,
|
|
18910
|
+
const memDir = (0, import_path32.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
|
|
18911
|
+
if (!(0, import_fs30.existsSync)(memDir)) return [];
|
|
18805
18912
|
const results = [];
|
|
18806
|
-
const knowledgeDir = (0,
|
|
18807
|
-
if ((0,
|
|
18808
|
-
const files = (0,
|
|
18913
|
+
const knowledgeDir = (0, import_path32.join)(memDir, "knowledge");
|
|
18914
|
+
if ((0, import_fs30.existsSync)(knowledgeDir)) {
|
|
18915
|
+
const files = (0, import_fs30.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
|
|
18809
18916
|
for (const file2 of files) {
|
|
18810
|
-
const filePath = (0,
|
|
18917
|
+
const filePath = (0, import_path32.join)(knowledgeDir, file2);
|
|
18811
18918
|
try {
|
|
18812
|
-
const content = (0,
|
|
18919
|
+
const content = (0, import_fs30.readFileSync)(filePath, "utf-8");
|
|
18813
18920
|
const frontmatter = this.parseFrontmatter(content);
|
|
18814
18921
|
const body = content.replace(/^---[\s\S]*?---\n*/, "");
|
|
18815
|
-
const name = frontmatter?.name || (0,
|
|
18922
|
+
const name = frontmatter?.name || (0, import_path32.basename)(file2, ".md");
|
|
18816
18923
|
const description = frontmatter?.description || "";
|
|
18817
18924
|
const importance = frontmatter?.importance ?? 0.5;
|
|
18818
18925
|
const score = this.scoreContent(keywords, name, description, body, importance);
|
|
@@ -18829,12 +18936,12 @@ var init_memory_searcher = __esm({
|
|
|
18829
18936
|
}
|
|
18830
18937
|
}
|
|
18831
18938
|
}
|
|
18832
|
-
const tasksPath = (0,
|
|
18833
|
-
if ((0,
|
|
18939
|
+
const tasksPath = (0, import_path32.join)(memDir, "tasks.jsonl");
|
|
18940
|
+
if ((0, import_fs30.existsSync)(tasksPath)) {
|
|
18834
18941
|
try {
|
|
18835
|
-
const stat3 = (0,
|
|
18942
|
+
const stat3 = (0, import_fs30.statSync)(tasksPath);
|
|
18836
18943
|
if (stat3.size > MAX_TASK_FILE_BYTES) return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
18837
|
-
const lines = (0,
|
|
18944
|
+
const lines = (0, import_fs30.readFileSync)(tasksPath, "utf-8").split("\n").filter((l) => l.trim());
|
|
18838
18945
|
for (const line of lines) {
|
|
18839
18946
|
try {
|
|
18840
18947
|
const entry = JSON.parse(line);
|
|
@@ -19120,9 +19227,9 @@ function prependScopeNote(prompt) {
|
|
|
19120
19227
|
}
|
|
19121
19228
|
function readSandboxMode(projectRoot) {
|
|
19122
19229
|
try {
|
|
19123
|
-
const p = (0,
|
|
19124
|
-
if (!(0,
|
|
19125
|
-
const raw = JSON.parse((0,
|
|
19230
|
+
const p = (0, import_path33.join)(projectRoot, ".gossip", "config.json");
|
|
19231
|
+
if (!(0, import_fs31.existsSync)(p)) return "warn";
|
|
19232
|
+
const raw = JSON.parse((0, import_fs31.readFileSync)(p, "utf-8"));
|
|
19126
19233
|
const mode = raw?.sandboxEnforcement;
|
|
19127
19234
|
if (mode === "off" || mode === "warn" || mode === "block") return mode;
|
|
19128
19235
|
return "warn";
|
|
@@ -19132,17 +19239,17 @@ function readSandboxMode(projectRoot) {
|
|
|
19132
19239
|
}
|
|
19133
19240
|
function recordDispatchMetadata(projectRoot, meta3) {
|
|
19134
19241
|
try {
|
|
19135
|
-
const dir = (0,
|
|
19136
|
-
(0,
|
|
19137
|
-
(0,
|
|
19242
|
+
const dir = (0, import_path33.join)(projectRoot, ".gossip");
|
|
19243
|
+
(0, import_fs31.mkdirSync)(dir, { recursive: true });
|
|
19244
|
+
(0, import_fs31.appendFileSync)((0, import_path33.join)(dir, METADATA_FILE), JSON.stringify(meta3) + "\n");
|
|
19138
19245
|
} catch {
|
|
19139
19246
|
}
|
|
19140
19247
|
}
|
|
19141
19248
|
function lookupDispatchMetadata(projectRoot, taskId) {
|
|
19142
19249
|
try {
|
|
19143
|
-
const p = (0,
|
|
19144
|
-
if (!(0,
|
|
19145
|
-
const raw = (0,
|
|
19250
|
+
const p = (0, import_path33.join)(projectRoot, ".gossip", METADATA_FILE);
|
|
19251
|
+
if (!(0, import_fs31.existsSync)(p)) return null;
|
|
19252
|
+
const raw = (0, import_fs31.readFileSync)(p, "utf-8");
|
|
19146
19253
|
const lines = raw.split("\n").filter(Boolean);
|
|
19147
19254
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
19148
19255
|
try {
|
|
@@ -19174,21 +19281,21 @@ function parseGitStatus(porcelain) {
|
|
|
19174
19281
|
function normalizeScope(scope, projectRoot) {
|
|
19175
19282
|
let s = scope.trim();
|
|
19176
19283
|
if (!s) return "";
|
|
19177
|
-
if ((0,
|
|
19178
|
-
s = (0,
|
|
19284
|
+
if ((0, import_path33.isAbsolute)(s)) {
|
|
19285
|
+
s = (0, import_path33.relative)(projectRoot, s);
|
|
19179
19286
|
} else if (s.startsWith("./")) {
|
|
19180
19287
|
s = s.slice(2);
|
|
19181
19288
|
}
|
|
19182
|
-
s = (0,
|
|
19289
|
+
s = (0, import_path33.normalize)(s).replace(/\/+$/, "");
|
|
19183
19290
|
if (s === "." || s === "") return "";
|
|
19184
19291
|
return s;
|
|
19185
19292
|
}
|
|
19186
19293
|
function isInsideScope(filePath, scope) {
|
|
19187
19294
|
if (!scope) return true;
|
|
19188
|
-
const f = (0,
|
|
19295
|
+
const f = (0, import_path33.normalize)(filePath).replace(/^\.\//, "");
|
|
19189
19296
|
const s = scope.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
19190
19297
|
if (f === s) return true;
|
|
19191
|
-
return f.startsWith(s + "/") || f.startsWith(s +
|
|
19298
|
+
return f.startsWith(s + "/") || f.startsWith(s + import_path33.sep);
|
|
19192
19299
|
}
|
|
19193
19300
|
function detectBoundaryEscapes(meta3, modifiedFiles, projectRoot) {
|
|
19194
19301
|
const mode = meta3.writeMode;
|
|
@@ -19250,8 +19357,8 @@ function recordBoundaryEscape(projectRoot, meta3, violations, mode) {
|
|
|
19250
19357
|
} catch {
|
|
19251
19358
|
}
|
|
19252
19359
|
try {
|
|
19253
|
-
const dir = (0,
|
|
19254
|
-
(0,
|
|
19360
|
+
const dir = (0, import_path33.join)(projectRoot, ".gossip");
|
|
19361
|
+
(0, import_fs31.mkdirSync)(dir, { recursive: true });
|
|
19255
19362
|
const line = {
|
|
19256
19363
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19257
19364
|
taskId: meta3.taskId,
|
|
@@ -19262,17 +19369,17 @@ function recordBoundaryEscape(projectRoot, meta3, violations, mode) {
|
|
|
19262
19369
|
action: mode
|
|
19263
19370
|
// "warn" or "block"
|
|
19264
19371
|
};
|
|
19265
|
-
(0,
|
|
19372
|
+
(0, import_fs31.appendFileSync)((0, import_path33.join)(dir, BOUNDARY_ESCAPE_FILE), JSON.stringify(line) + "\n");
|
|
19266
19373
|
} catch {
|
|
19267
19374
|
}
|
|
19268
19375
|
}
|
|
19269
|
-
var import_child_process4,
|
|
19376
|
+
var import_child_process4, import_fs31, import_path33, METADATA_FILE, BOUNDARY_ESCAPE_FILE, SYSTEM_PREFIXES, SCOPE_NOTE, __test__;
|
|
19270
19377
|
var init_sandbox2 = __esm({
|
|
19271
19378
|
"apps/cli/src/sandbox.ts"() {
|
|
19272
19379
|
"use strict";
|
|
19273
19380
|
import_child_process4 = require("child_process");
|
|
19274
|
-
|
|
19275
|
-
|
|
19381
|
+
import_fs31 = require("fs");
|
|
19382
|
+
import_path33 = require("path");
|
|
19276
19383
|
METADATA_FILE = "dispatch-metadata.jsonl";
|
|
19277
19384
|
BOUNDARY_ESCAPE_FILE = "boundary-escapes.jsonl";
|
|
19278
19385
|
SYSTEM_PREFIXES = [
|
|
@@ -19356,11 +19463,11 @@ function startConsensusTimeout(consensusId) {
|
|
|
19356
19463
|
const allEntries = [...snapshot.relayCrossReviewEntries, ...snapshot.nativeCrossReviewEntries];
|
|
19357
19464
|
const report = await engine.synthesizeWithCrossReview(snapshot.allResults, allEntries, consensusId, snapshot.relayCrossReviewSkipped);
|
|
19358
19465
|
try {
|
|
19359
|
-
const { writeFileSync:
|
|
19360
|
-
const { join:
|
|
19361
|
-
const reportsDir =
|
|
19362
|
-
|
|
19363
|
-
|
|
19466
|
+
const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22 } = require("fs");
|
|
19467
|
+
const { join: join49 } = require("path");
|
|
19468
|
+
const reportsDir = join49(process.cwd(), ".gossip", "consensus-reports");
|
|
19469
|
+
mkdirSync22(reportsDir, { recursive: true });
|
|
19470
|
+
writeFileSync18(join49(reportsDir, `${consensusId}.json`), JSON.stringify({
|
|
19364
19471
|
id: consensusId,
|
|
19365
19472
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19366
19473
|
agentCount: report.agentCount,
|
|
@@ -19405,6 +19512,10 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
|
|
|
19405
19512
|
round.pendingNativeAgents.delete(agent_id);
|
|
19406
19513
|
process.stderr.write(`[gossipcat] \u{1F4E8} Cross-review received from ${agent_id}. Remaining: ${round.pendingNativeAgents.size}
|
|
19407
19514
|
`);
|
|
19515
|
+
let parsedCount = 0;
|
|
19516
|
+
let acceptedCount = 0;
|
|
19517
|
+
const rejectedPeerIds = /* @__PURE__ */ new Set();
|
|
19518
|
+
let parseError = null;
|
|
19408
19519
|
try {
|
|
19409
19520
|
const { ConsensusEngine: ConsensusEngine2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
|
|
19410
19521
|
const parseLlm = ctx.mainAgent.getLlm();
|
|
@@ -19414,14 +19525,51 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
|
|
|
19414
19525
|
projectRoot: process.cwd()
|
|
19415
19526
|
});
|
|
19416
19527
|
const entries = engine.parseCrossReviewResponse(agent_id, result, 50);
|
|
19528
|
+
parsedCount = entries.length;
|
|
19417
19529
|
const validPeerIds = new Set(round.allResults.map((r) => r.agentId));
|
|
19418
|
-
const filtered = entries.filter((e) =>
|
|
19530
|
+
const filtered = entries.filter((e) => {
|
|
19531
|
+
const selfReview = e.peerAgentId === agent_id;
|
|
19532
|
+
const unknownPeer = !validPeerIds.has(e.peerAgentId);
|
|
19533
|
+
if (selfReview || unknownPeer) {
|
|
19534
|
+
if (e.peerAgentId) rejectedPeerIds.add(e.peerAgentId);
|
|
19535
|
+
return false;
|
|
19536
|
+
}
|
|
19537
|
+
return true;
|
|
19538
|
+
});
|
|
19539
|
+
acceptedCount = filtered.length;
|
|
19419
19540
|
round.nativeCrossReviewEntries.push(...filtered);
|
|
19541
|
+
if (parsedCount > 0 && acceptedCount === 0) {
|
|
19542
|
+
process.stderr.write(
|
|
19543
|
+
`[gossipcat] \u26A0\uFE0F Cross-review from ${agent_id}: all ${parsedCount} entries rejected. Bad peer IDs: [${[...rejectedPeerIds].join(", ")}]. Valid round members: [${[...validPeerIds].join(", ")}]. Expected findingId format "<peerAgentId>:f<N>".
|
|
19544
|
+
`
|
|
19545
|
+
);
|
|
19546
|
+
} else if (parsedCount > acceptedCount) {
|
|
19547
|
+
process.stderr.write(
|
|
19548
|
+
`[gossipcat] \u26A0\uFE0F Cross-review from ${agent_id}: ${parsedCount - acceptedCount}/${parsedCount} entries rejected (bad peer IDs: [${[...rejectedPeerIds].join(", ")}]).
|
|
19549
|
+
`
|
|
19550
|
+
);
|
|
19551
|
+
}
|
|
19420
19552
|
} catch (err) {
|
|
19421
|
-
|
|
19553
|
+
parseError = err.message;
|
|
19554
|
+
process.stderr.write(`[gossipcat] Failed to parse cross-review from ${agent_id}: ${parseError}
|
|
19422
19555
|
`);
|
|
19423
19556
|
}
|
|
19424
19557
|
persistPendingConsensus();
|
|
19558
|
+
const validPeerList = [...new Set(round.allResults.map((r) => r.agentId))].join(", ");
|
|
19559
|
+
let diagnostic = "";
|
|
19560
|
+
if (parseError) {
|
|
19561
|
+
diagnostic = `
|
|
19562
|
+
\u26A0\uFE0F Parse error: ${parseError}`;
|
|
19563
|
+
} else if (parsedCount > 0 && acceptedCount === 0) {
|
|
19564
|
+
diagnostic = `
|
|
19565
|
+
\u26A0\uFE0F All ${parsedCount} entries were REJECTED. Bad peer IDs: [${[...rejectedPeerIds].join(", ")}]. Valid round members: [${validPeerList}]. Expected findingId format "<peerAgentId>:f<N>" using exact agent IDs from the round.`;
|
|
19566
|
+
} else if (parsedCount > acceptedCount) {
|
|
19567
|
+
diagnostic = `
|
|
19568
|
+
\u26A0\uFE0F ${parsedCount - acceptedCount}/${parsedCount} entries rejected (bad peer IDs: [${[...rejectedPeerIds].join(", ")}]). Valid round members: [${validPeerList}].`;
|
|
19569
|
+
} else if (parsedCount > 0) {
|
|
19570
|
+
diagnostic = `
|
|
19571
|
+
\u2705 ${acceptedCount}/${parsedCount} entries accepted.`;
|
|
19572
|
+
}
|
|
19425
19573
|
if (round.pendingNativeAgents.size > 0) {
|
|
19426
19574
|
const EXTENSION_MS = 6e5;
|
|
19427
19575
|
const MAX_TOTAL_MS = 36e5;
|
|
@@ -19430,7 +19578,7 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
|
|
|
19430
19578
|
return {
|
|
19431
19579
|
content: [{
|
|
19432
19580
|
type: "text",
|
|
19433
|
-
text: `Cross-review from ${agent_id} received. Waiting for ${round.pendingNativeAgents.size} more agent(s): ${[...round.pendingNativeAgents].join(", ")}`
|
|
19581
|
+
text: `Cross-review from ${agent_id} received. Waiting for ${round.pendingNativeAgents.size} more agent(s): ${[...round.pendingNativeAgents].join(", ")}${diagnostic}`
|
|
19434
19582
|
}]
|
|
19435
19583
|
};
|
|
19436
19584
|
}
|
|
@@ -19467,12 +19615,12 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
|
|
|
19467
19615
|
synthSnapshot.relayCrossReviewSkipped
|
|
19468
19616
|
);
|
|
19469
19617
|
try {
|
|
19470
|
-
const { writeFileSync:
|
|
19471
|
-
const { join:
|
|
19472
|
-
const reportsDir =
|
|
19473
|
-
|
|
19474
|
-
const reportPath =
|
|
19475
|
-
|
|
19618
|
+
const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22 } = require("fs");
|
|
19619
|
+
const { join: join49 } = require("path");
|
|
19620
|
+
const reportsDir = join49(process.cwd(), ".gossip", "consensus-reports");
|
|
19621
|
+
mkdirSync22(reportsDir, { recursive: true });
|
|
19622
|
+
const reportPath = join49(reportsDir, `${consensus_id}.json`);
|
|
19623
|
+
writeFileSync18(reportPath, JSON.stringify({
|
|
19476
19624
|
id: consensus_id,
|
|
19477
19625
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19478
19626
|
agentCount: report.agentCount,
|
|
@@ -19512,10 +19660,10 @@ function persistPendingConsensus() {
|
|
|
19512
19660
|
try {
|
|
19513
19661
|
const projectRoot = ctx.mainAgent?.projectRoot;
|
|
19514
19662
|
if (!projectRoot) return;
|
|
19515
|
-
const { writeFileSync:
|
|
19516
|
-
const { join:
|
|
19517
|
-
const dir =
|
|
19518
|
-
|
|
19663
|
+
const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22 } = require("fs");
|
|
19664
|
+
const { join: join49 } = require("path");
|
|
19665
|
+
const dir = join49(projectRoot, ".gossip");
|
|
19666
|
+
mkdirSync22(dir, { recursive: true });
|
|
19519
19667
|
const rounds = {};
|
|
19520
19668
|
for (const [id, round] of ctx.pendingConsensusRounds) {
|
|
19521
19669
|
rounds[id] = {
|
|
@@ -19537,7 +19685,7 @@ function persistPendingConsensus() {
|
|
|
19537
19685
|
nativePrompts: (round.nativePrompts || []).filter((p) => round.pendingNativeAgents.has(p.agentId))
|
|
19538
19686
|
};
|
|
19539
19687
|
}
|
|
19540
|
-
|
|
19688
|
+
writeFileSync18(join49(dir, CONSENSUS_FILE), JSON.stringify(rounds));
|
|
19541
19689
|
} catch (err) {
|
|
19542
19690
|
process.stderr.write(`[gossipcat] persistPendingConsensus failed: ${err.message}
|
|
19543
19691
|
`);
|
|
@@ -19545,11 +19693,11 @@ function persistPendingConsensus() {
|
|
|
19545
19693
|
}
|
|
19546
19694
|
function restorePendingConsensus(projectRoot) {
|
|
19547
19695
|
try {
|
|
19548
|
-
const { existsSync:
|
|
19549
|
-
const { join:
|
|
19550
|
-
const filePath =
|
|
19551
|
-
if (!
|
|
19552
|
-
const raw = JSON.parse(
|
|
19696
|
+
const { existsSync: existsSync44, readFileSync: readFileSync41, unlinkSync: unlinkSync4 } = require("fs");
|
|
19697
|
+
const { join: join49 } = require("path");
|
|
19698
|
+
const filePath = join49(projectRoot, ".gossip", CONSENSUS_FILE);
|
|
19699
|
+
if (!existsSync44(filePath)) return;
|
|
19700
|
+
const raw = JSON.parse(readFileSync41(filePath, "utf-8"));
|
|
19553
19701
|
const now = Date.now();
|
|
19554
19702
|
for (const [id, data] of Object.entries(raw)) {
|
|
19555
19703
|
if (now > data.deadline + 3e5) {
|
|
@@ -19592,15 +19740,15 @@ __export(check_effectiveness_runner_exports, {
|
|
|
19592
19740
|
runCheckEffectivenessForAllSkills: () => runCheckEffectivenessForAllSkills
|
|
19593
19741
|
});
|
|
19594
19742
|
async function runCheckEffectivenessForAllSkills(opts) {
|
|
19595
|
-
const baseDir = (0,
|
|
19596
|
-
if (!(0,
|
|
19597
|
-
const agentDirs = (0,
|
|
19743
|
+
const baseDir = (0, import_path35.join)(opts.projectRoot, ".gossip", "agents");
|
|
19744
|
+
if (!(0, import_fs33.existsSync)(baseDir)) return;
|
|
19745
|
+
const agentDirs = (0, import_fs33.readdirSync)(baseDir);
|
|
19598
19746
|
for (const agentId of agentDirs) {
|
|
19599
|
-
const skillsDir = (0,
|
|
19600
|
-
if (!(0,
|
|
19747
|
+
const skillsDir = (0, import_path35.join)(baseDir, agentId, "skills");
|
|
19748
|
+
if (!(0, import_fs33.existsSync)(skillsDir)) continue;
|
|
19601
19749
|
const role = opts.registryGet(agentId)?.role;
|
|
19602
19750
|
if (role === "implementer") continue;
|
|
19603
|
-
const files = (0,
|
|
19751
|
+
const files = (0, import_fs33.readdirSync)(skillsDir).filter((f) => f.endsWith(".md"));
|
|
19604
19752
|
for (const file2 of files) {
|
|
19605
19753
|
const category = file2.replace(/\.md$/, "");
|
|
19606
19754
|
try {
|
|
@@ -19621,12 +19769,12 @@ async function runCheckEffectivenessForAllSkills(opts) {
|
|
|
19621
19769
|
}
|
|
19622
19770
|
}
|
|
19623
19771
|
}
|
|
19624
|
-
var
|
|
19772
|
+
var import_fs33, import_path35;
|
|
19625
19773
|
var init_check_effectiveness_runner = __esm({
|
|
19626
19774
|
"apps/cli/src/handlers/check-effectiveness-runner.ts"() {
|
|
19627
19775
|
"use strict";
|
|
19628
|
-
|
|
19629
|
-
|
|
19776
|
+
import_fs33 = require("fs");
|
|
19777
|
+
import_path35 = require("path");
|
|
19630
19778
|
}
|
|
19631
19779
|
});
|
|
19632
19780
|
|
|
@@ -20309,12 +20457,12 @@ async function overviewHandler(projectRoot, ctx2) {
|
|
|
20309
20457
|
const hourlyActivity = new Array(12).fill(0);
|
|
20310
20458
|
const now = Date.now();
|
|
20311
20459
|
const hourMs = 60 * 60 * 1e3;
|
|
20312
|
-
const graphPath = (0,
|
|
20313
|
-
if ((0,
|
|
20460
|
+
const graphPath = (0, import_path37.join)(projectRoot, ".gossip", "task-graph.jsonl");
|
|
20461
|
+
if ((0, import_fs35.existsSync)(graphPath)) {
|
|
20314
20462
|
try {
|
|
20315
20463
|
const created = /* @__PURE__ */ new Map();
|
|
20316
20464
|
const finished = /* @__PURE__ */ new Set();
|
|
20317
|
-
const lines = (0,
|
|
20465
|
+
const lines = (0, import_fs35.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
20318
20466
|
for (const line of lines) {
|
|
20319
20467
|
try {
|
|
20320
20468
|
const ev = JSON.parse(line);
|
|
@@ -20364,17 +20512,27 @@ async function overviewHandler(projectRoot, ctx2) {
|
|
|
20364
20512
|
let confirmedFindings = 0;
|
|
20365
20513
|
let lastConsensusTimestamp = "";
|
|
20366
20514
|
let actionableFindings = 0;
|
|
20367
|
-
const
|
|
20368
|
-
const perfPath = (0,
|
|
20369
|
-
if ((0,
|
|
20515
|
+
const runBuckets = /* @__PURE__ */ new Map();
|
|
20516
|
+
const perfPath = (0, import_path37.join)(projectRoot, ".gossip", "agent-performance.jsonl");
|
|
20517
|
+
if ((0, import_fs35.existsSync)(perfPath)) {
|
|
20370
20518
|
try {
|
|
20371
|
-
const lines = (0,
|
|
20519
|
+
const lines = (0, import_fs35.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
20372
20520
|
for (const line of lines) {
|
|
20373
20521
|
try {
|
|
20374
20522
|
const entry = JSON.parse(line);
|
|
20375
|
-
|
|
20523
|
+
if (entry.type === "consensus" && typeof entry.signal === "string") {
|
|
20524
|
+
totalSignals++;
|
|
20525
|
+
}
|
|
20376
20526
|
if (entry.type === "consensus" && (entry.consensusId || entry.taskId)) {
|
|
20377
|
-
|
|
20527
|
+
const runId = entry.consensusId ?? entry.taskId;
|
|
20528
|
+
let bucket = runBuckets.get(runId);
|
|
20529
|
+
if (!bucket) {
|
|
20530
|
+
bucket = { agents: /* @__PURE__ */ new Set(), signalCount: 0 };
|
|
20531
|
+
runBuckets.set(runId, bucket);
|
|
20532
|
+
}
|
|
20533
|
+
bucket.signalCount++;
|
|
20534
|
+
if (entry.agentId) bucket.agents.add(entry.agentId);
|
|
20535
|
+
if (entry.counterpartId) bucket.agents.add(entry.counterpartId);
|
|
20378
20536
|
}
|
|
20379
20537
|
if (entry.consensusId && entry.timestamp > lastConsensusTimestamp) {
|
|
20380
20538
|
lastConsensusTimestamp = entry.timestamp;
|
|
@@ -20397,27 +20555,29 @@ async function overviewHandler(projectRoot, ctx2) {
|
|
|
20397
20555
|
} catch {
|
|
20398
20556
|
}
|
|
20399
20557
|
}
|
|
20400
|
-
|
|
20558
|
+
for (const bucket of runBuckets.values()) {
|
|
20559
|
+
if (bucket.agents.size >= 2 && bucket.signalCount >= 3) consensusRuns++;
|
|
20560
|
+
}
|
|
20401
20561
|
const avgDurationMs = durationCount > 0 ? Math.round(totalDuration / durationCount) : 0;
|
|
20402
20562
|
return { agentsOnline, relayCount, relayConnected, nativeCount, consensusRuns, totalFindings, confirmedFindings, totalSignals, tasksCompleted, tasksFailed, avgDurationMs, lastConsensusTimestamp, actionableFindings, hourlyActivity };
|
|
20403
20563
|
}
|
|
20404
|
-
var
|
|
20564
|
+
var import_fs35, import_path37;
|
|
20405
20565
|
var init_api_overview = __esm({
|
|
20406
20566
|
"packages/relay/src/dashboard/api-overview.ts"() {
|
|
20407
20567
|
"use strict";
|
|
20408
|
-
|
|
20409
|
-
|
|
20568
|
+
import_fs35 = require("fs");
|
|
20569
|
+
import_path37 = require("path");
|
|
20410
20570
|
}
|
|
20411
20571
|
});
|
|
20412
20572
|
|
|
20413
20573
|
// packages/relay/src/dashboard/api-agents.ts
|
|
20414
20574
|
function readTaskGraphByAgent(projectRoot) {
|
|
20415
|
-
const taskGraphPath = (0,
|
|
20575
|
+
const taskGraphPath = (0, import_path38.join)(projectRoot, ".gossip", "task-graph.jsonl");
|
|
20416
20576
|
const result = /* @__PURE__ */ new Map();
|
|
20417
|
-
if (!(0,
|
|
20577
|
+
if (!(0, import_fs36.existsSync)(taskGraphPath)) return result;
|
|
20418
20578
|
let lines;
|
|
20419
20579
|
try {
|
|
20420
|
-
lines = (0,
|
|
20580
|
+
lines = (0, import_fs36.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
20421
20581
|
} catch {
|
|
20422
20582
|
return result;
|
|
20423
20583
|
}
|
|
@@ -20509,19 +20669,32 @@ async function agentsHandler(projectRoot, configs, onlineAgents = []) {
|
|
|
20509
20669
|
hallucinations: score.hallucinations,
|
|
20510
20670
|
consecutiveFailures: score.consecutiveFailures ?? 0,
|
|
20511
20671
|
circuitOpen: score.circuitOpen ?? false,
|
|
20512
|
-
|
|
20672
|
+
// categoryStrengths is an UNBOUNDED severity-weighted accumulator used
|
|
20673
|
+
// for dispatch routing (severity × decay × 0.15 per confirmed signal),
|
|
20674
|
+
// not a [0,1] ratio. The dashboard must NOT render it as a percentage —
|
|
20675
|
+
// it should render categoryAccuracy (c / (c + h)) which is a real ratio.
|
|
20676
|
+
// See performance-reader.ts:357 (increment) vs :496-505 (accuracy).
|
|
20677
|
+
categoryStrengths: score.categoryStrengths ?? {},
|
|
20678
|
+
categoryAccuracy: score.categoryAccuracy ?? {},
|
|
20679
|
+
// Forward raw counts so the dashboard can render "100% (3/3)" and
|
|
20680
|
+
// distinguish a sparse-but-clean category from a high-volume clean
|
|
20681
|
+
// one. categoryAccuracy already drops categories with fewer than
|
|
20682
|
+
// MIN_CATEGORY_N signals, but the counts let the UI surface them
|
|
20683
|
+
// as dimmed "sparse" rows instead of hiding them silently.
|
|
20684
|
+
categoryCorrect: score.categoryCorrect ?? {},
|
|
20685
|
+
categoryHallucinated: score.categoryHallucinated ?? {}
|
|
20513
20686
|
}
|
|
20514
20687
|
};
|
|
20515
20688
|
});
|
|
20516
20689
|
}
|
|
20517
|
-
var
|
|
20690
|
+
var import_fs36, import_path38, DEFAULT_SCORE;
|
|
20518
20691
|
var init_api_agents = __esm({
|
|
20519
20692
|
"packages/relay/src/dashboard/api-agents.ts"() {
|
|
20520
20693
|
"use strict";
|
|
20521
20694
|
init_performance_reader();
|
|
20522
20695
|
init_skill_index();
|
|
20523
|
-
|
|
20524
|
-
|
|
20696
|
+
import_fs36 = require("fs");
|
|
20697
|
+
import_path38 = require("path");
|
|
20525
20698
|
DEFAULT_SCORE = {
|
|
20526
20699
|
agentId: "",
|
|
20527
20700
|
accuracy: 0.5,
|
|
@@ -20545,7 +20718,7 @@ var init_api_agents = __esm({
|
|
|
20545
20718
|
|
|
20546
20719
|
// packages/relay/src/dashboard/api-skills.ts
|
|
20547
20720
|
function isCorrupt(projectRoot, index) {
|
|
20548
|
-
return (0,
|
|
20721
|
+
return (0, import_fs37.existsSync)((0, import_path39.join)(projectRoot, ".gossip", "skill-index.json")) && !index.exists();
|
|
20549
20722
|
}
|
|
20550
20723
|
async function skillsGetHandler(projectRoot) {
|
|
20551
20724
|
try {
|
|
@@ -20574,13 +20747,13 @@ async function skillsBindHandler(projectRoot, body) {
|
|
|
20574
20747
|
return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
|
|
20575
20748
|
}
|
|
20576
20749
|
}
|
|
20577
|
-
var
|
|
20750
|
+
var import_fs37, import_path39, AGENT_ID_RE2;
|
|
20578
20751
|
var init_api_skills = __esm({
|
|
20579
20752
|
"packages/relay/src/dashboard/api-skills.ts"() {
|
|
20580
20753
|
"use strict";
|
|
20581
20754
|
init_skill_index();
|
|
20582
|
-
|
|
20583
|
-
|
|
20755
|
+
import_fs37 = require("fs");
|
|
20756
|
+
import_path39 = require("path");
|
|
20584
20757
|
AGENT_ID_RE2 = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
20585
20758
|
}
|
|
20586
20759
|
});
|
|
@@ -20588,25 +20761,25 @@ var init_api_skills = __esm({
|
|
|
20588
20761
|
// packages/relay/src/dashboard/api-memory.ts
|
|
20589
20762
|
async function memoryHandler(projectRoot, agentId) {
|
|
20590
20763
|
if (!agentId || !AGENT_ID_RE3.test(agentId) || DANGEROUS_IDS.has(agentId)) throw new Error("Invalid agent ID");
|
|
20591
|
-
const memDir = (0,
|
|
20764
|
+
const memDir = (0, import_path40.join)(projectRoot, ".gossip", "agents", agentId, "memory");
|
|
20592
20765
|
let index = "";
|
|
20593
|
-
const indexPath = (0,
|
|
20594
|
-
if ((0,
|
|
20766
|
+
const indexPath = (0, import_path40.join)(memDir, "MEMORY.md");
|
|
20767
|
+
if ((0, import_fs38.existsSync)(indexPath)) {
|
|
20595
20768
|
try {
|
|
20596
|
-
index = (0,
|
|
20769
|
+
index = (0, import_fs38.readFileSync)(indexPath, "utf-8");
|
|
20597
20770
|
} catch {
|
|
20598
20771
|
}
|
|
20599
20772
|
}
|
|
20600
20773
|
const knowledge = [];
|
|
20601
|
-
const knowledgeDir = (0,
|
|
20774
|
+
const knowledgeDir = (0, import_path40.join)(memDir, "knowledge");
|
|
20602
20775
|
const knowledgeDirs = [knowledgeDir, memDir];
|
|
20603
20776
|
for (const dir of knowledgeDirs) {
|
|
20604
|
-
if (!(0,
|
|
20777
|
+
if (!(0, import_fs38.existsSync)(dir)) continue;
|
|
20605
20778
|
try {
|
|
20606
|
-
const files = (0,
|
|
20779
|
+
const files = (0, import_fs38.readdirSync)(dir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
|
|
20607
20780
|
for (const filename of files) {
|
|
20608
20781
|
try {
|
|
20609
|
-
const raw = (0,
|
|
20782
|
+
const raw = (0, import_fs38.readFileSync)((0, import_path40.join)(dir, filename), "utf-8");
|
|
20610
20783
|
const { frontmatter, content } = parseFrontmatter(raw);
|
|
20611
20784
|
knowledge.push({ filename, frontmatter, content });
|
|
20612
20785
|
} catch {
|
|
@@ -20616,10 +20789,10 @@ async function memoryHandler(projectRoot, agentId) {
|
|
|
20616
20789
|
}
|
|
20617
20790
|
}
|
|
20618
20791
|
const tasks = [];
|
|
20619
|
-
const tasksPath = (0,
|
|
20620
|
-
if ((0,
|
|
20792
|
+
const tasksPath = (0, import_path40.join)(memDir, "tasks.jsonl");
|
|
20793
|
+
if ((0, import_fs38.existsSync)(tasksPath)) {
|
|
20621
20794
|
try {
|
|
20622
|
-
const lines = (0,
|
|
20795
|
+
const lines = (0, import_fs38.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean).slice(-200);
|
|
20623
20796
|
for (const line of lines) {
|
|
20624
20797
|
try {
|
|
20625
20798
|
tasks.push(JSON.parse(line));
|
|
@@ -20647,12 +20820,12 @@ function parseFrontmatter(raw) {
|
|
|
20647
20820
|
}
|
|
20648
20821
|
return { frontmatter: fm, content: raw.slice(end + 3).trim() };
|
|
20649
20822
|
}
|
|
20650
|
-
var
|
|
20823
|
+
var import_fs38, import_path40, AGENT_ID_RE3, DANGEROUS_IDS;
|
|
20651
20824
|
var init_api_memory = __esm({
|
|
20652
20825
|
"packages/relay/src/dashboard/api-memory.ts"() {
|
|
20653
20826
|
"use strict";
|
|
20654
|
-
|
|
20655
|
-
|
|
20827
|
+
import_fs38 = require("fs");
|
|
20828
|
+
import_path40 = require("path");
|
|
20656
20829
|
AGENT_ID_RE3 = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
20657
20830
|
DANGEROUS_IDS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
20658
20831
|
}
|
|
@@ -20664,11 +20837,11 @@ async function consensusHandler(projectRoot, query) {
|
|
|
20664
20837
|
const rawPageSize = parseInt(query?.get("pageSize") ?? "", 10);
|
|
20665
20838
|
const page = isNaN(rawPage) || rawPage < 1 ? 1 : rawPage;
|
|
20666
20839
|
const pageSize = isNaN(rawPageSize) || rawPageSize < 1 ? DEFAULT_PAGE_SIZE : Math.min(rawPageSize, MAX_PAGE_SIZE);
|
|
20667
|
-
const perfPath = (0,
|
|
20668
|
-
if (!(0,
|
|
20840
|
+
const perfPath = (0, import_path41.join)(projectRoot, ".gossip", "agent-performance.jsonl");
|
|
20841
|
+
if (!(0, import_fs39.existsSync)(perfPath)) return { runs: [], totalRuns: 0, totalSignals: 0, page, pageSize };
|
|
20669
20842
|
const signals = [];
|
|
20670
20843
|
try {
|
|
20671
|
-
const lines = (0,
|
|
20844
|
+
const lines = (0, import_fs39.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
20672
20845
|
for (const line of lines) {
|
|
20673
20846
|
try {
|
|
20674
20847
|
const parsed = JSON.parse(line);
|
|
@@ -20739,12 +20912,12 @@ async function consensusHandler(projectRoot, query) {
|
|
|
20739
20912
|
const paginatedRuns = runs.slice(offset, offset + pageSize);
|
|
20740
20913
|
return { runs: paginatedRuns, totalRuns, totalSignals: signals.length, page, pageSize };
|
|
20741
20914
|
}
|
|
20742
|
-
var
|
|
20915
|
+
var import_fs39, import_path41, RESOLUTION_SIGNALS, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE;
|
|
20743
20916
|
var init_api_consensus = __esm({
|
|
20744
20917
|
"packages/relay/src/dashboard/api-consensus.ts"() {
|
|
20745
20918
|
"use strict";
|
|
20746
|
-
|
|
20747
|
-
|
|
20919
|
+
import_fs39 = require("fs");
|
|
20920
|
+
import_path41 = require("path");
|
|
20748
20921
|
RESOLUTION_SIGNALS = /* @__PURE__ */ new Set(["agreement", "unique_confirmed", "consensus_verified"]);
|
|
20749
20922
|
DEFAULT_PAGE_SIZE = 10;
|
|
20750
20923
|
MAX_PAGE_SIZE = 50;
|
|
@@ -20756,11 +20929,11 @@ async function signalsHandler(projectRoot, query) {
|
|
|
20756
20929
|
const agentFilter = query?.get("agent") ?? null;
|
|
20757
20930
|
const limit = Math.min(Math.max(parseInt(query?.get("limit") ?? "", 10) || DEFAULT_LIMIT, 1), MAX_LIMIT);
|
|
20758
20931
|
const offset = Math.max(parseInt(query?.get("offset") ?? "", 10) || 0, 0);
|
|
20759
|
-
const perfPath = (0,
|
|
20760
|
-
if (!(0,
|
|
20932
|
+
const perfPath = (0, import_path42.join)(projectRoot, ".gossip", "agent-performance.jsonl");
|
|
20933
|
+
if (!(0, import_fs40.existsSync)(perfPath)) return { items: [], total: 0, offset, limit };
|
|
20761
20934
|
const all = [];
|
|
20762
20935
|
try {
|
|
20763
|
-
const lines = (0,
|
|
20936
|
+
const lines = (0, import_fs40.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
20764
20937
|
for (const line of lines) {
|
|
20765
20938
|
try {
|
|
20766
20939
|
const entry = JSON.parse(line);
|
|
@@ -20776,12 +20949,12 @@ async function signalsHandler(projectRoot, query) {
|
|
|
20776
20949
|
all.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
20777
20950
|
return { items: all.slice(offset, offset + limit), total: all.length, offset, limit };
|
|
20778
20951
|
}
|
|
20779
|
-
var
|
|
20952
|
+
var import_fs40, import_path42, MAX_LIMIT, DEFAULT_LIMIT;
|
|
20780
20953
|
var init_api_signals = __esm({
|
|
20781
20954
|
"packages/relay/src/dashboard/api-signals.ts"() {
|
|
20782
20955
|
"use strict";
|
|
20783
|
-
|
|
20784
|
-
|
|
20956
|
+
import_fs40 = require("fs");
|
|
20957
|
+
import_path42 = require("path");
|
|
20785
20958
|
MAX_LIMIT = 200;
|
|
20786
20959
|
DEFAULT_LIMIT = 50;
|
|
20787
20960
|
}
|
|
@@ -20789,29 +20962,29 @@ var init_api_signals = __esm({
|
|
|
20789
20962
|
|
|
20790
20963
|
// packages/relay/src/dashboard/api-learnings.ts
|
|
20791
20964
|
async function learningsHandler(projectRoot) {
|
|
20792
|
-
const agentsDir = (0,
|
|
20793
|
-
if (!(0,
|
|
20965
|
+
const agentsDir = (0, import_path43.join)(projectRoot, ".gossip", "agents");
|
|
20966
|
+
if (!(0, import_fs41.existsSync)(agentsDir)) return { learnings: [] };
|
|
20794
20967
|
const all = [];
|
|
20795
20968
|
let agentIds;
|
|
20796
20969
|
try {
|
|
20797
|
-
agentIds = (0,
|
|
20970
|
+
agentIds = (0, import_fs41.readdirSync)(agentsDir).filter((f) => !f.startsWith("."));
|
|
20798
20971
|
} catch {
|
|
20799
20972
|
return { learnings: [] };
|
|
20800
20973
|
}
|
|
20801
20974
|
for (const agentId of agentIds) {
|
|
20802
|
-
const knowledgeDir = (0,
|
|
20803
|
-
if (!(0,
|
|
20975
|
+
const knowledgeDir = (0, import_path43.join)(agentsDir, agentId, "memory", "knowledge");
|
|
20976
|
+
if (!(0, import_fs41.existsSync)(knowledgeDir)) continue;
|
|
20804
20977
|
let files;
|
|
20805
20978
|
try {
|
|
20806
|
-
files = (0,
|
|
20979
|
+
files = (0, import_fs41.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
|
|
20807
20980
|
} catch {
|
|
20808
20981
|
continue;
|
|
20809
20982
|
}
|
|
20810
20983
|
for (const filename of files) {
|
|
20811
|
-
const filepath = (0,
|
|
20984
|
+
const filepath = (0, import_path43.join)(knowledgeDir, filename);
|
|
20812
20985
|
try {
|
|
20813
|
-
const stat3 = (0,
|
|
20814
|
-
const raw = (0,
|
|
20986
|
+
const stat3 = (0, import_fs41.statSync)(filepath);
|
|
20987
|
+
const raw = (0, import_fs41.readFileSync)(filepath, "utf-8");
|
|
20815
20988
|
const fm = parseFrontmatter2(raw);
|
|
20816
20989
|
all.push({
|
|
20817
20990
|
agentId,
|
|
@@ -20838,12 +21011,12 @@ function parseFrontmatter2(raw) {
|
|
|
20838
21011
|
}
|
|
20839
21012
|
return fm;
|
|
20840
21013
|
}
|
|
20841
|
-
var
|
|
21014
|
+
var import_fs41, import_path43, MAX_LEARNINGS;
|
|
20842
21015
|
var init_api_learnings = __esm({
|
|
20843
21016
|
"packages/relay/src/dashboard/api-learnings.ts"() {
|
|
20844
21017
|
"use strict";
|
|
20845
|
-
|
|
20846
|
-
|
|
21018
|
+
import_fs41 = require("fs");
|
|
21019
|
+
import_path43 = require("path");
|
|
20847
21020
|
MAX_LEARNINGS = 10;
|
|
20848
21021
|
}
|
|
20849
21022
|
});
|
|
@@ -20854,12 +21027,12 @@ async function tasksHandler(projectRoot, query) {
|
|
|
20854
21027
|
const rawOffset = parseInt(query?.get("offset") ?? "0", 10);
|
|
20855
21028
|
const limit = isNaN(rawLimit) || rawLimit < 1 ? 50 : Math.min(rawLimit, 200);
|
|
20856
21029
|
const offset = isNaN(rawOffset) || rawOffset < 0 ? 0 : rawOffset;
|
|
20857
|
-
const graphPath = (0,
|
|
20858
|
-
if (!(0,
|
|
21030
|
+
const graphPath = (0, import_path44.join)(projectRoot, ".gossip", "task-graph.jsonl");
|
|
21031
|
+
if (!(0, import_fs42.existsSync)(graphPath)) return { items: [], total: 0, offset, limit };
|
|
20859
21032
|
const created = /* @__PURE__ */ new Map();
|
|
20860
21033
|
const completed = /* @__PURE__ */ new Map();
|
|
20861
21034
|
try {
|
|
20862
|
-
const lines = (0,
|
|
21035
|
+
const lines = (0, import_fs42.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
20863
21036
|
for (const line of lines) {
|
|
20864
21037
|
try {
|
|
20865
21038
|
const entry = JSON.parse(line);
|
|
@@ -20914,23 +21087,23 @@ async function tasksHandler(projectRoot, query) {
|
|
|
20914
21087
|
tasks.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
20915
21088
|
return { items: tasks.slice(offset, offset + limit), total: tasks.length, offset, limit };
|
|
20916
21089
|
}
|
|
20917
|
-
var
|
|
21090
|
+
var import_fs42, import_path44;
|
|
20918
21091
|
var init_api_tasks = __esm({
|
|
20919
21092
|
"packages/relay/src/dashboard/api-tasks.ts"() {
|
|
20920
21093
|
"use strict";
|
|
20921
|
-
|
|
20922
|
-
|
|
21094
|
+
import_fs42 = require("fs");
|
|
21095
|
+
import_path44 = require("path");
|
|
20923
21096
|
}
|
|
20924
21097
|
});
|
|
20925
21098
|
|
|
20926
21099
|
// packages/relay/src/dashboard/api-active-tasks.ts
|
|
20927
21100
|
async function activeTasksHandler(projectRoot) {
|
|
20928
|
-
const taskGraphPath = (0,
|
|
20929
|
-
if (!(0,
|
|
21101
|
+
const taskGraphPath = (0, import_path45.join)(projectRoot, ".gossip", "task-graph.jsonl");
|
|
21102
|
+
if (!(0, import_fs43.existsSync)(taskGraphPath)) return { tasks: [] };
|
|
20930
21103
|
const created = /* @__PURE__ */ new Map();
|
|
20931
21104
|
const finished = /* @__PURE__ */ new Set();
|
|
20932
21105
|
try {
|
|
20933
|
-
const lines = (0,
|
|
21106
|
+
const lines = (0, import_fs43.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
20934
21107
|
for (const line of lines) {
|
|
20935
21108
|
try {
|
|
20936
21109
|
const ev = JSON.parse(line);
|
|
@@ -20957,12 +21130,12 @@ async function activeTasksHandler(projectRoot) {
|
|
|
20957
21130
|
active.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
20958
21131
|
return { tasks: active.slice(0, 10) };
|
|
20959
21132
|
}
|
|
20960
|
-
var
|
|
21133
|
+
var import_fs43, import_path45;
|
|
20961
21134
|
var init_api_active_tasks = __esm({
|
|
20962
21135
|
"packages/relay/src/dashboard/api-active-tasks.ts"() {
|
|
20963
21136
|
"use strict";
|
|
20964
|
-
|
|
20965
|
-
|
|
21137
|
+
import_fs43 = require("fs");
|
|
21138
|
+
import_path45 = require("path");
|
|
20966
21139
|
}
|
|
20967
21140
|
});
|
|
20968
21141
|
|
|
@@ -20975,25 +21148,25 @@ function categorize(text) {
|
|
|
20975
21148
|
return "other";
|
|
20976
21149
|
}
|
|
20977
21150
|
function logsHandler(projectRoot, query) {
|
|
20978
|
-
const logPath = (0,
|
|
20979
|
-
if (!(0,
|
|
21151
|
+
const logPath = (0, import_path46.join)(projectRoot, ".gossip", "mcp.log");
|
|
21152
|
+
if (!(0, import_fs44.existsSync)(logPath)) {
|
|
20980
21153
|
return { entries: [], totalLines: 0, fileSize: 0 };
|
|
20981
21154
|
}
|
|
20982
21155
|
const filter = query?.get("filter") || void 0;
|
|
20983
21156
|
const tail = parseInt(query?.get("tail") || "200", 10);
|
|
20984
21157
|
const clampedTail = Math.min(Math.max(tail, 10), 2e3);
|
|
20985
|
-
const fileSize = (0,
|
|
21158
|
+
const fileSize = (0, import_fs44.statSync)(logPath).size;
|
|
20986
21159
|
const MAX_READ = 512 * 1024;
|
|
20987
21160
|
const readFrom = Math.max(0, fileSize - MAX_READ);
|
|
20988
21161
|
const readLen = fileSize - readFrom;
|
|
20989
|
-
const fd = (0,
|
|
21162
|
+
const fd = (0, import_fs44.openSync)(logPath, "r");
|
|
20990
21163
|
let buf = Buffer.alloc(0);
|
|
20991
21164
|
try {
|
|
20992
21165
|
buf = Buffer.allocUnsafe(readLen);
|
|
20993
|
-
const bytesRead = (0,
|
|
21166
|
+
const bytesRead = (0, import_fs44.readSync)(fd, buf, 0, readLen, readFrom);
|
|
20994
21167
|
buf = buf.subarray(0, bytesRead);
|
|
20995
21168
|
} finally {
|
|
20996
|
-
(0,
|
|
21169
|
+
(0, import_fs44.closeSync)(fd);
|
|
20997
21170
|
}
|
|
20998
21171
|
let raw = buf.toString("utf-8");
|
|
20999
21172
|
let lineOffset = 0;
|
|
@@ -21001,13 +21174,13 @@ function logsHandler(projectRoot, query) {
|
|
|
21001
21174
|
const nl = raw.indexOf("\n");
|
|
21002
21175
|
raw = nl >= 0 ? raw.slice(nl + 1) : raw;
|
|
21003
21176
|
const SCAN_CHUNK = 64 * 1024;
|
|
21004
|
-
const scanFd = (0,
|
|
21177
|
+
const scanFd = (0, import_fs44.openSync)(logPath, "r");
|
|
21005
21178
|
try {
|
|
21006
21179
|
let pos = 0;
|
|
21007
21180
|
const chunk = Buffer.allocUnsafe(SCAN_CHUNK);
|
|
21008
21181
|
while (pos < readFrom) {
|
|
21009
21182
|
const len = Math.min(SCAN_CHUNK, readFrom - pos);
|
|
21010
|
-
const n = (0,
|
|
21183
|
+
const n = (0, import_fs44.readSync)(scanFd, chunk, 0, len, pos);
|
|
21011
21184
|
if (n === 0) break;
|
|
21012
21185
|
for (let j = 0; j < n; j++) {
|
|
21013
21186
|
if (chunk[j] === 10) lineOffset++;
|
|
@@ -21015,7 +21188,7 @@ function logsHandler(projectRoot, query) {
|
|
|
21015
21188
|
pos += n;
|
|
21016
21189
|
}
|
|
21017
21190
|
} finally {
|
|
21018
|
-
(0,
|
|
21191
|
+
(0, import_fs44.closeSync)(scanFd);
|
|
21019
21192
|
}
|
|
21020
21193
|
}
|
|
21021
21194
|
const allLines = raw.split("\n").filter(Boolean);
|
|
@@ -21033,12 +21206,12 @@ function logsHandler(projectRoot, query) {
|
|
|
21033
21206
|
entries = entries.slice(-clampedTail);
|
|
21034
21207
|
return { entries, totalLines, fileSize, filter };
|
|
21035
21208
|
}
|
|
21036
|
-
var
|
|
21209
|
+
var import_fs44, import_path46, CATEGORY_PATTERNS2;
|
|
21037
21210
|
var init_api_logs = __esm({
|
|
21038
21211
|
"packages/relay/src/dashboard/api-logs.ts"() {
|
|
21039
21212
|
"use strict";
|
|
21040
|
-
|
|
21041
|
-
|
|
21213
|
+
import_fs44 = require("fs");
|
|
21214
|
+
import_path46 = require("path");
|
|
21042
21215
|
CATEGORY_PATTERNS2 = [
|
|
21043
21216
|
[/^\[worker:/, "worker"],
|
|
21044
21217
|
[/^\[Gemini\]/, "gemini"],
|
|
@@ -21066,8 +21239,22 @@ var init_api_logs = __esm({
|
|
|
21066
21239
|
});
|
|
21067
21240
|
|
|
21068
21241
|
// packages/relay/src/dashboard/routes.ts
|
|
21242
|
+
function resolveDashboardRoot(projectRoot) {
|
|
21243
|
+
const candidates = [
|
|
21244
|
+
(0, import_path47.resolve)(__dirname, "..", "dist-dashboard"),
|
|
21245
|
+
// bundled: dist-mcp/mcp-server.js → ../dist-dashboard
|
|
21246
|
+
(0, import_path47.resolve)(__dirname, "..", "..", "..", "..", "dist-dashboard"),
|
|
21247
|
+
// tsc dev: packages/relay/dist/dashboard → repo-root
|
|
21248
|
+
(0, import_path47.join)(projectRoot, "dist-dashboard")
|
|
21249
|
+
// legacy dev fallback (git-clone running from repo root)
|
|
21250
|
+
];
|
|
21251
|
+
for (const p of candidates) {
|
|
21252
|
+
if ((0, import_fs45.existsSync)(p)) return p;
|
|
21253
|
+
}
|
|
21254
|
+
return null;
|
|
21255
|
+
}
|
|
21069
21256
|
function readBody(req) {
|
|
21070
|
-
return new Promise((
|
|
21257
|
+
return new Promise((resolve18, reject) => {
|
|
21071
21258
|
const chunks = [];
|
|
21072
21259
|
let size = 0;
|
|
21073
21260
|
let tooLarge = false;
|
|
@@ -21082,14 +21269,14 @@ function readBody(req) {
|
|
|
21082
21269
|
chunks.push(chunk);
|
|
21083
21270
|
});
|
|
21084
21271
|
req.on("end", () => {
|
|
21085
|
-
if (!tooLarge)
|
|
21272
|
+
if (!tooLarge) resolve18(Buffer.concat(chunks).toString("utf-8"));
|
|
21086
21273
|
});
|
|
21087
21274
|
req.on("error", (err) => {
|
|
21088
21275
|
if (!tooLarge) reject(err);
|
|
21089
21276
|
});
|
|
21090
21277
|
});
|
|
21091
21278
|
}
|
|
21092
|
-
var
|
|
21279
|
+
var import_fs45, import_path47, AUTH_MAX_ATTEMPTS, AUTH_LOCKOUT_MS, DashboardRouter, MAX_BODY_SIZE;
|
|
21093
21280
|
var init_routes = __esm({
|
|
21094
21281
|
"packages/relay/src/dashboard/routes.ts"() {
|
|
21095
21282
|
"use strict";
|
|
@@ -21103,8 +21290,8 @@ var init_routes = __esm({
|
|
|
21103
21290
|
init_api_tasks();
|
|
21104
21291
|
init_api_active_tasks();
|
|
21105
21292
|
init_api_logs();
|
|
21106
|
-
|
|
21107
|
-
|
|
21293
|
+
import_fs45 = require("fs");
|
|
21294
|
+
import_path47 = require("path");
|
|
21108
21295
|
AUTH_MAX_ATTEMPTS = 10;
|
|
21109
21296
|
AUTH_LOCKOUT_MS = 6e4;
|
|
21110
21297
|
DashboardRouter = class {
|
|
@@ -21112,8 +21299,10 @@ var init_routes = __esm({
|
|
|
21112
21299
|
this.auth = auth;
|
|
21113
21300
|
this.projectRoot = projectRoot;
|
|
21114
21301
|
this.ctx = ctx2;
|
|
21302
|
+
this.dashboardRoot = resolveDashboardRoot(projectRoot);
|
|
21115
21303
|
}
|
|
21116
21304
|
authAttempts = /* @__PURE__ */ new Map();
|
|
21305
|
+
dashboardRoot;
|
|
21117
21306
|
/** Update live context (call when agents connect/disconnect) */
|
|
21118
21307
|
updateContext(ctx2) {
|
|
21119
21308
|
if (ctx2.agentConfigs !== void 0) this.ctx.agentConfigs = ctx2.agentConfigs;
|
|
@@ -21279,18 +21468,24 @@ var init_routes = __esm({
|
|
|
21279
21468
|
return true;
|
|
21280
21469
|
}
|
|
21281
21470
|
serveDashboard(res) {
|
|
21282
|
-
|
|
21283
|
-
if (!(0, import_fs43.existsSync)(htmlPath)) {
|
|
21471
|
+
if (!this.dashboardRoot) {
|
|
21284
21472
|
res.writeHead(503, { "Content-Type": "text/plain" });
|
|
21285
|
-
res.end("Dashboard not
|
|
21473
|
+
res.end("Dashboard assets not found. Reinstall gossipcat or rebuild from source.");
|
|
21286
21474
|
return true;
|
|
21287
21475
|
}
|
|
21288
|
-
const
|
|
21476
|
+
const htmlPath = (0, import_path47.join)(this.dashboardRoot, "index.html");
|
|
21477
|
+
if (!(0, import_fs45.existsSync)(htmlPath)) {
|
|
21478
|
+
res.writeHead(503, { "Content-Type": "text/plain" });
|
|
21479
|
+
res.end(`Dashboard index.html missing at ${this.dashboardRoot}. Reinstall gossipcat.`);
|
|
21480
|
+
return true;
|
|
21481
|
+
}
|
|
21482
|
+
const html = (0, import_fs45.readFileSync)(htmlPath, "utf-8");
|
|
21289
21483
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
21290
21484
|
res.end(html);
|
|
21291
21485
|
return true;
|
|
21292
21486
|
}
|
|
21293
21487
|
serveStaticFile(res, url2) {
|
|
21488
|
+
if (!this.dashboardRoot) return false;
|
|
21294
21489
|
const relativePath = url2.replace(/^\/dashboard\//, "");
|
|
21295
21490
|
if (relativePath.includes("..")) {
|
|
21296
21491
|
res.writeHead(404);
|
|
@@ -21310,16 +21505,16 @@ var init_routes = __esm({
|
|
|
21310
21505
|
const ext = "." + (relativePath.split(".").pop() || "");
|
|
21311
21506
|
const mime = MIME[ext];
|
|
21312
21507
|
if (!mime) return false;
|
|
21313
|
-
const filePath = (0,
|
|
21508
|
+
const filePath = (0, import_path47.join)(this.dashboardRoot, relativePath);
|
|
21314
21509
|
try {
|
|
21315
|
-
const realFile = (0,
|
|
21316
|
-
const realBase = (0,
|
|
21510
|
+
const realFile = (0, import_fs45.realpathSync)(filePath);
|
|
21511
|
+
const realBase = (0, import_fs45.realpathSync)(this.dashboardRoot);
|
|
21317
21512
|
if (!realFile.startsWith(realBase + "/")) {
|
|
21318
21513
|
res.writeHead(404);
|
|
21319
21514
|
res.end();
|
|
21320
21515
|
return true;
|
|
21321
21516
|
}
|
|
21322
|
-
const data = (0,
|
|
21517
|
+
const data = (0, import_fs45.readFileSync)(realFile);
|
|
21323
21518
|
res.writeHead(200, { "Content-Type": mime, "Cache-Control": "public, max-age=86400" });
|
|
21324
21519
|
res.end(data);
|
|
21325
21520
|
return true;
|
|
@@ -21334,15 +21529,15 @@ var init_routes = __esm({
|
|
|
21334
21529
|
return match ? match[1] : null;
|
|
21335
21530
|
}
|
|
21336
21531
|
getConsensusReports(page = 1, pageSize = 5) {
|
|
21337
|
-
const { readdirSync: readdirSync13, readFileSync:
|
|
21338
|
-
const reportsDir = (0,
|
|
21339
|
-
if (!
|
|
21532
|
+
const { readdirSync: readdirSync13, readFileSync: readFileSync41, existsSync: existsSync44 } = require("fs");
|
|
21533
|
+
const reportsDir = (0, import_path47.join)(this.projectRoot, ".gossip", "consensus-reports");
|
|
21534
|
+
if (!existsSync44(reportsDir)) return { reports: [], totalReports: 0, page, pageSize };
|
|
21340
21535
|
try {
|
|
21341
21536
|
const { statSync: statSync9 } = require("fs");
|
|
21342
21537
|
const allFiles = readdirSync13(reportsDir).filter((f) => f.endsWith(".json")).sort((a, b) => {
|
|
21343
21538
|
try {
|
|
21344
|
-
const aTime = statSync9((0,
|
|
21345
|
-
const bTime = statSync9((0,
|
|
21539
|
+
const aTime = statSync9((0, import_path47.join)(reportsDir, a)).mtimeMs;
|
|
21540
|
+
const bTime = statSync9((0, import_path47.join)(reportsDir, b)).mtimeMs;
|
|
21346
21541
|
return bTime - aTime;
|
|
21347
21542
|
} catch {
|
|
21348
21543
|
return 0;
|
|
@@ -21353,13 +21548,13 @@ var init_routes = __esm({
|
|
|
21353
21548
|
const clampedPage = Math.max(page, 1);
|
|
21354
21549
|
const start = (clampedPage - 1) * clampedPageSize;
|
|
21355
21550
|
const files = allFiles.slice(start, start + clampedPageSize);
|
|
21356
|
-
const realReportsDir = (0,
|
|
21551
|
+
const realReportsDir = (0, import_fs45.realpathSync)(reportsDir);
|
|
21357
21552
|
const reports = files.map((f) => {
|
|
21358
21553
|
try {
|
|
21359
|
-
const filePath = (0,
|
|
21360
|
-
const realFile = (0,
|
|
21554
|
+
const filePath = (0, import_path47.join)(reportsDir, f);
|
|
21555
|
+
const realFile = (0, import_fs45.realpathSync)(filePath);
|
|
21361
21556
|
if (!realFile.startsWith(realReportsDir + "/")) return null;
|
|
21362
|
-
return JSON.parse(
|
|
21557
|
+
return JSON.parse(readFileSync41(realFile, "utf-8"));
|
|
21363
21558
|
} catch {
|
|
21364
21559
|
return null;
|
|
21365
21560
|
}
|
|
@@ -21370,29 +21565,29 @@ var init_routes = __esm({
|
|
|
21370
21565
|
}
|
|
21371
21566
|
}
|
|
21372
21567
|
archiveFindings() {
|
|
21373
|
-
const { readdirSync: readdirSync13, readFileSync:
|
|
21374
|
-
const reportsDir = (0,
|
|
21375
|
-
const archiveDir = (0,
|
|
21568
|
+
const { readdirSync: readdirSync13, readFileSync: readFileSync41, renameSync: renameSync2, writeFileSync: writeFileSync18, mkdirSync: mkdirSync22, existsSync: existsSync44 } = require("fs");
|
|
21569
|
+
const reportsDir = (0, import_path47.join)(this.projectRoot, ".gossip", "consensus-reports");
|
|
21570
|
+
const archiveDir = (0, import_path47.join)(this.projectRoot, ".gossip", "consensus-reports-archive");
|
|
21376
21571
|
let archived = 0;
|
|
21377
|
-
if (
|
|
21572
|
+
if (existsSync44(reportsDir)) {
|
|
21378
21573
|
const files = readdirSync13(reportsDir).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
21379
21574
|
if (files.length > 5) {
|
|
21380
|
-
|
|
21575
|
+
mkdirSync22(archiveDir, { recursive: true });
|
|
21381
21576
|
const toArchive = files.slice(5);
|
|
21382
21577
|
for (const f of toArchive) {
|
|
21383
21578
|
try {
|
|
21384
|
-
renameSync2((0,
|
|
21579
|
+
renameSync2((0, import_path47.join)(reportsDir, f), (0, import_path47.join)(archiveDir, f));
|
|
21385
21580
|
archived++;
|
|
21386
21581
|
} catch {
|
|
21387
21582
|
}
|
|
21388
21583
|
}
|
|
21389
21584
|
}
|
|
21390
21585
|
}
|
|
21391
|
-
const findingsPath = (0,
|
|
21586
|
+
const findingsPath = (0, import_path47.join)(this.projectRoot, ".gossip", "implementation-findings.jsonl");
|
|
21392
21587
|
let findingsCleared = 0;
|
|
21393
|
-
if (
|
|
21588
|
+
if (existsSync44(findingsPath)) {
|
|
21394
21589
|
try {
|
|
21395
|
-
const lines =
|
|
21590
|
+
const lines = readFileSync41(findingsPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
21396
21591
|
const kept = lines.filter((line) => {
|
|
21397
21592
|
try {
|
|
21398
21593
|
const entry = JSON.parse(line);
|
|
@@ -21405,11 +21600,11 @@ var init_routes = __esm({
|
|
|
21405
21600
|
return true;
|
|
21406
21601
|
}
|
|
21407
21602
|
});
|
|
21408
|
-
|
|
21603
|
+
writeFileSync18(findingsPath, kept.join("\n") + (kept.length > 0 ? "\n" : ""));
|
|
21409
21604
|
} catch {
|
|
21410
21605
|
}
|
|
21411
21606
|
}
|
|
21412
|
-
const remaining =
|
|
21607
|
+
const remaining = existsSync44(reportsDir) ? readdirSync13(reportsDir).filter((f) => f.endsWith(".json")).length : 0;
|
|
21413
21608
|
return { archived, remaining, findingsCleared };
|
|
21414
21609
|
}
|
|
21415
21610
|
json(res, status, data) {
|
|
@@ -21422,14 +21617,14 @@ var init_routes = __esm({
|
|
|
21422
21617
|
});
|
|
21423
21618
|
|
|
21424
21619
|
// packages/relay/src/dashboard/ws.ts
|
|
21425
|
-
var import_ws3,
|
|
21620
|
+
var import_ws3, import_fs46, import_fs47, import_path48, DashboardWs;
|
|
21426
21621
|
var init_ws = __esm({
|
|
21427
21622
|
"packages/relay/src/dashboard/ws.ts"() {
|
|
21428
21623
|
"use strict";
|
|
21429
21624
|
import_ws3 = require("ws");
|
|
21430
|
-
|
|
21431
|
-
|
|
21432
|
-
|
|
21625
|
+
import_fs46 = require("fs");
|
|
21626
|
+
import_fs47 = require("fs");
|
|
21627
|
+
import_path48 = require("path");
|
|
21433
21628
|
DashboardWs = class {
|
|
21434
21629
|
clients = /* @__PURE__ */ new Set();
|
|
21435
21630
|
logWatcher = null;
|
|
@@ -21454,15 +21649,15 @@ var init_ws = __esm({
|
|
|
21454
21649
|
/** Start watching mcp.log for new lines and broadcasting them to connected clients. */
|
|
21455
21650
|
startLogWatcher(projectRoot) {
|
|
21456
21651
|
this.stopLogWatcher();
|
|
21457
|
-
this.logPath = (0,
|
|
21458
|
-
if (!(0,
|
|
21652
|
+
this.logPath = (0, import_path48.join)(projectRoot, ".gossip", "mcp.log");
|
|
21653
|
+
if (!(0, import_fs46.existsSync)(this.logPath)) return;
|
|
21459
21654
|
try {
|
|
21460
|
-
this.logOffset = (0,
|
|
21655
|
+
this.logOffset = (0, import_fs46.statSync)(this.logPath).size;
|
|
21461
21656
|
} catch {
|
|
21462
21657
|
this.logOffset = 0;
|
|
21463
21658
|
}
|
|
21464
21659
|
try {
|
|
21465
|
-
this.logWatcher = (0,
|
|
21660
|
+
this.logWatcher = (0, import_fs47.watch)(this.logPath, () => {
|
|
21466
21661
|
if (this.clients.size === 0) return;
|
|
21467
21662
|
this.readNewLines();
|
|
21468
21663
|
});
|
|
@@ -21479,7 +21674,7 @@ var init_ws = __esm({
|
|
|
21479
21674
|
if (this.logReading) return;
|
|
21480
21675
|
this.logReading = true;
|
|
21481
21676
|
try {
|
|
21482
|
-
const currentSize = (0,
|
|
21677
|
+
const currentSize = (0, import_fs46.statSync)(this.logPath).size;
|
|
21483
21678
|
if (currentSize < this.logOffset) {
|
|
21484
21679
|
this.logOffset = 0;
|
|
21485
21680
|
this.logCarry = "";
|
|
@@ -21492,7 +21687,7 @@ var init_ws = __esm({
|
|
|
21492
21687
|
const readFrom = capped ? (this.logCarry = "", this.logCapped = true, currentSize - 65536) : (this.logCapped = false, this.logOffset);
|
|
21493
21688
|
let stream;
|
|
21494
21689
|
try {
|
|
21495
|
-
stream = (0,
|
|
21690
|
+
stream = (0, import_fs46.createReadStream)(this.logPath, { start: readFrom, end: currentSize - 1 });
|
|
21496
21691
|
} catch {
|
|
21497
21692
|
this.logReading = false;
|
|
21498
21693
|
return;
|
|
@@ -21586,7 +21781,7 @@ var init_server = __esm({
|
|
|
21586
21781
|
return `ws://localhost:${this._port}`;
|
|
21587
21782
|
}
|
|
21588
21783
|
async start() {
|
|
21589
|
-
return new Promise((
|
|
21784
|
+
return new Promise((resolve18) => {
|
|
21590
21785
|
this.httpServer = (0, import_http.createServer)(this.handleHttp.bind(this));
|
|
21591
21786
|
if (this.config.dashboard) {
|
|
21592
21787
|
this.dashboardAuth = new DashboardAuth();
|
|
@@ -21631,7 +21826,7 @@ var init_server = __esm({
|
|
|
21631
21826
|
this.httpServer.listen(this.config.port, this.config.host ?? "127.0.0.1", () => {
|
|
21632
21827
|
const addr = this.httpServer.address();
|
|
21633
21828
|
this._port = addr.port;
|
|
21634
|
-
|
|
21829
|
+
resolve18();
|
|
21635
21830
|
});
|
|
21636
21831
|
});
|
|
21637
21832
|
}
|
|
@@ -21650,9 +21845,9 @@ var init_server = __esm({
|
|
|
21650
21845
|
for (const client of this.wss.clients) {
|
|
21651
21846
|
client.close(1001, "Server shutting down");
|
|
21652
21847
|
}
|
|
21653
|
-
return new Promise((
|
|
21848
|
+
return new Promise((resolve18) => {
|
|
21654
21849
|
this.wss.close(() => {
|
|
21655
|
-
this.httpServer.close(() =>
|
|
21850
|
+
this.httpServer.close(() => resolve18());
|
|
21656
21851
|
});
|
|
21657
21852
|
});
|
|
21658
21853
|
}
|
|
@@ -21848,18 +22043,18 @@ __export(config_exports, {
|
|
|
21848
22043
|
function findConfigPath(projectRoot) {
|
|
21849
22044
|
const root = projectRoot || process.cwd();
|
|
21850
22045
|
const candidates = [
|
|
21851
|
-
(0,
|
|
21852
|
-
(0,
|
|
21853
|
-
(0,
|
|
21854
|
-
(0,
|
|
22046
|
+
(0, import_path49.resolve)(root, ".gossip", "config.json"),
|
|
22047
|
+
(0, import_path49.resolve)(root, "gossip.agents.json"),
|
|
22048
|
+
(0, import_path49.resolve)(root, "gossip.agents.yaml"),
|
|
22049
|
+
(0, import_path49.resolve)(root, "gossip.agents.yml")
|
|
21855
22050
|
];
|
|
21856
22051
|
for (const p of candidates) {
|
|
21857
|
-
if ((0,
|
|
22052
|
+
if ((0, import_fs48.existsSync)(p)) return p;
|
|
21858
22053
|
}
|
|
21859
22054
|
return null;
|
|
21860
22055
|
}
|
|
21861
22056
|
function loadConfig(configPath) {
|
|
21862
|
-
const raw = (0,
|
|
22057
|
+
const raw = (0, import_fs48.readFileSync)(configPath, "utf-8");
|
|
21863
22058
|
let parsed;
|
|
21864
22059
|
try {
|
|
21865
22060
|
parsed = JSON.parse(raw);
|
|
@@ -21938,19 +22133,19 @@ function configToAgentConfigs(config2) {
|
|
|
21938
22133
|
}
|
|
21939
22134
|
function loadClaudeSubagents(projectRoot, existingIds) {
|
|
21940
22135
|
const root = projectRoot || process.cwd();
|
|
21941
|
-
const agentsDir = (0,
|
|
21942
|
-
if (!(0,
|
|
22136
|
+
const agentsDir = (0, import_path49.join)(root, ".claude", "agents");
|
|
22137
|
+
if (!(0, import_fs48.existsSync)(agentsDir)) return [];
|
|
21943
22138
|
let files;
|
|
21944
22139
|
try {
|
|
21945
|
-
files = (0,
|
|
22140
|
+
files = (0, import_fs48.readdirSync)(agentsDir).filter((f) => f.endsWith(".md"));
|
|
21946
22141
|
} catch {
|
|
21947
22142
|
return [];
|
|
21948
22143
|
}
|
|
21949
22144
|
const agents = [];
|
|
21950
22145
|
for (const file2 of files) {
|
|
21951
|
-
const filePath = (0,
|
|
22146
|
+
const filePath = (0, import_path49.join)(agentsDir, file2);
|
|
21952
22147
|
try {
|
|
21953
|
-
const content = (0,
|
|
22148
|
+
const content = (0, import_fs48.readFileSync)(filePath, "utf-8");
|
|
21954
22149
|
const frontmatter = content.match(/^---\n([\s\S]*?)\n---/);
|
|
21955
22150
|
if (!frontmatter) continue;
|
|
21956
22151
|
const fm = frontmatter[1];
|
|
@@ -22005,12 +22200,12 @@ function inferSkills(description, name) {
|
|
|
22005
22200
|
if (skills.length === 0) skills.push("general");
|
|
22006
22201
|
return skills;
|
|
22007
22202
|
}
|
|
22008
|
-
var
|
|
22203
|
+
var import_fs48, import_path49, VALID_PROVIDERS, CLAUDE_MODEL_MAP;
|
|
22009
22204
|
var init_config = __esm({
|
|
22010
22205
|
"apps/cli/src/config.ts"() {
|
|
22011
22206
|
"use strict";
|
|
22012
|
-
|
|
22013
|
-
|
|
22207
|
+
import_fs48 = require("fs");
|
|
22208
|
+
import_path49 = require("path");
|
|
22014
22209
|
VALID_PROVIDERS = ["anthropic", "openai", "openclaw", "google", "local", "native"];
|
|
22015
22210
|
CLAUDE_MODEL_MAP = {
|
|
22016
22211
|
opus: { provider: "anthropic", model: "claude-opus-4-6" },
|
|
@@ -22025,14 +22220,14 @@ var keychain_exports = {};
|
|
|
22025
22220
|
__export(keychain_exports, {
|
|
22026
22221
|
Keychain: () => Keychain
|
|
22027
22222
|
});
|
|
22028
|
-
var import_child_process5, import_os2,
|
|
22223
|
+
var import_child_process5, import_os2, import_fs49, import_path50, import_crypto15, SERVICE_NAME, VALID_PROVIDERS2, ENCRYPTED_FILE, ALGO, Keychain;
|
|
22029
22224
|
var init_keychain = __esm({
|
|
22030
22225
|
"apps/cli/src/keychain.ts"() {
|
|
22031
22226
|
"use strict";
|
|
22032
22227
|
import_child_process5 = require("child_process");
|
|
22033
22228
|
import_os2 = require("os");
|
|
22034
|
-
|
|
22035
|
-
|
|
22229
|
+
import_fs49 = require("fs");
|
|
22230
|
+
import_path50 = require("path");
|
|
22036
22231
|
import_crypto15 = require("crypto");
|
|
22037
22232
|
SERVICE_NAME = "gossip-mesh";
|
|
22038
22233
|
VALID_PROVIDERS2 = /^[a-zA-Z0-9_-]{1,32}$/;
|
|
@@ -22074,10 +22269,10 @@ var init_keychain = __esm({
|
|
|
22074
22269
|
return (0, import_crypto15.pbkdf2Sync)(seed, salt, 6e5, 32, "sha256");
|
|
22075
22270
|
}
|
|
22076
22271
|
loadEncryptedFile() {
|
|
22077
|
-
const filePath = (0,
|
|
22078
|
-
if (!(0,
|
|
22272
|
+
const filePath = (0, import_path50.join)(process.cwd(), ENCRYPTED_FILE);
|
|
22273
|
+
if (!(0, import_fs49.existsSync)(filePath)) return;
|
|
22079
22274
|
try {
|
|
22080
|
-
const raw = (0,
|
|
22275
|
+
const raw = (0, import_fs49.readFileSync)(filePath);
|
|
22081
22276
|
if (raw.length < 61) return;
|
|
22082
22277
|
const salt = raw.subarray(0, 32);
|
|
22083
22278
|
const iv = raw.subarray(32, 44);
|
|
@@ -22095,9 +22290,9 @@ var init_keychain = __esm({
|
|
|
22095
22290
|
}
|
|
22096
22291
|
}
|
|
22097
22292
|
saveEncryptedFile() {
|
|
22098
|
-
const filePath = (0,
|
|
22099
|
-
const dir = (0,
|
|
22100
|
-
if (!(0,
|
|
22293
|
+
const filePath = (0, import_path50.join)(process.cwd(), ENCRYPTED_FILE);
|
|
22294
|
+
const dir = (0, import_path50.join)(process.cwd(), ".gossip");
|
|
22295
|
+
if (!(0, import_fs49.existsSync)(dir)) (0, import_fs49.mkdirSync)(dir, { recursive: true });
|
|
22101
22296
|
const data = JSON.stringify(Object.fromEntries(this.inMemoryStore));
|
|
22102
22297
|
const salt = (0, import_crypto15.randomBytes)(32);
|
|
22103
22298
|
const iv = (0, import_crypto15.randomBytes)(12);
|
|
@@ -22105,7 +22300,7 @@ var init_keychain = __esm({
|
|
|
22105
22300
|
const cipher = (0, import_crypto15.createCipheriv)(ALGO, key, iv);
|
|
22106
22301
|
const encrypted = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
|
|
22107
22302
|
const tag = cipher.getAuthTag();
|
|
22108
|
-
(0,
|
|
22303
|
+
(0, import_fs49.writeFileSync)(filePath, Buffer.concat([salt, iv, tag, encrypted]), { mode: 384 });
|
|
22109
22304
|
}
|
|
22110
22305
|
isKeychainAvailable() {
|
|
22111
22306
|
if ((0, import_os2.platform)() === "darwin") {
|
|
@@ -22205,17 +22400,17 @@ __export(identity_exports, {
|
|
|
22205
22400
|
normalizeGitUrl: () => normalizeGitUrl
|
|
22206
22401
|
});
|
|
22207
22402
|
function getOrCreateSalt(projectRoot) {
|
|
22208
|
-
const saltPath = (0,
|
|
22403
|
+
const saltPath = (0, import_path51.join)(projectRoot, ".gossip", "local-salt");
|
|
22209
22404
|
try {
|
|
22210
|
-
return (0,
|
|
22405
|
+
return (0, import_fs50.readFileSync)(saltPath, "utf-8").trim();
|
|
22211
22406
|
} catch {
|
|
22212
22407
|
const salt = (0, import_crypto16.randomBytes)(16).toString("hex");
|
|
22213
|
-
(0,
|
|
22408
|
+
(0, import_fs50.mkdirSync)((0, import_path51.join)(projectRoot, ".gossip"), { recursive: true });
|
|
22214
22409
|
try {
|
|
22215
|
-
(0,
|
|
22410
|
+
(0, import_fs50.writeFileSync)(saltPath, salt, { flag: "wx" });
|
|
22216
22411
|
return salt;
|
|
22217
22412
|
} catch {
|
|
22218
|
-
return (0,
|
|
22413
|
+
return (0, import_fs50.readFileSync)(saltPath, "utf-8").trim();
|
|
22219
22414
|
}
|
|
22220
22415
|
}
|
|
22221
22416
|
}
|
|
@@ -22265,12 +22460,12 @@ function getProjectId(projectRoot) {
|
|
|
22265
22460
|
}
|
|
22266
22461
|
return (0, import_crypto16.createHash)("sha256").update(projectRoot).digest("hex").slice(0, 16);
|
|
22267
22462
|
}
|
|
22268
|
-
var
|
|
22463
|
+
var import_fs50, import_path51, import_crypto16, import_child_process6;
|
|
22269
22464
|
var init_identity = __esm({
|
|
22270
22465
|
"apps/cli/src/identity.ts"() {
|
|
22271
22466
|
"use strict";
|
|
22272
|
-
|
|
22273
|
-
|
|
22467
|
+
import_fs50 = require("fs");
|
|
22468
|
+
import_path51 = require("path");
|
|
22274
22469
|
import_crypto16 = require("crypto");
|
|
22275
22470
|
import_child_process6 = require("child_process");
|
|
22276
22471
|
}
|
|
@@ -22282,14 +22477,8 @@ __export(gossip_update_exports, {
|
|
|
22282
22477
|
handleGossipUpdate: () => handleGossipUpdate
|
|
22283
22478
|
});
|
|
22284
22479
|
function getCurrentVersion() {
|
|
22285
|
-
|
|
22286
|
-
|
|
22287
|
-
if ((0, import_fs49.existsSync)(pkgPath)) {
|
|
22288
|
-
return JSON.parse((0, import_fs49.readFileSync)(pkgPath, "utf-8")).version ?? "0.0.0";
|
|
22289
|
-
}
|
|
22290
|
-
} catch {
|
|
22291
|
-
}
|
|
22292
|
-
return "0.0.0";
|
|
22480
|
+
const v = getGossipcatVersion();
|
|
22481
|
+
return v === "unknown" ? "0.0.0" : v;
|
|
22293
22482
|
}
|
|
22294
22483
|
async function getLatestVersion() {
|
|
22295
22484
|
const res = await fetch("https://registry.npmjs.org/gossipcat/latest");
|
|
@@ -22298,9 +22487,9 @@ async function getLatestVersion() {
|
|
|
22298
22487
|
return data.version;
|
|
22299
22488
|
}
|
|
22300
22489
|
function detectInstallMethod() {
|
|
22301
|
-
const packageRoot = (0,
|
|
22490
|
+
const packageRoot = (0, import_path52.resolve)(__dirname, "..", "..", "..", "..");
|
|
22302
22491
|
if (process.env.npm_config_global === "true") return "global";
|
|
22303
|
-
if ((0,
|
|
22492
|
+
if ((0, import_fs51.existsSync)((0, import_path52.join)(packageRoot, ".git"))) return "git-clone";
|
|
22304
22493
|
return "local";
|
|
22305
22494
|
}
|
|
22306
22495
|
function updateCommand(method, version2) {
|
|
@@ -22351,7 +22540,7 @@ Check your internet connection or visit https://www.npmjs.com/package/gossipcat
|
|
|
22351
22540
|
try {
|
|
22352
22541
|
(0, import_child_process7.execSync)(command, {
|
|
22353
22542
|
stdio: "inherit",
|
|
22354
|
-
cwd: method === "git-clone" ? (0,
|
|
22543
|
+
cwd: method === "git-clone" ? (0, import_path52.resolve)(__dirname, "..", "..", "..", "..") : process.cwd()
|
|
22355
22544
|
});
|
|
22356
22545
|
} catch (err) {
|
|
22357
22546
|
return {
|
|
@@ -22373,13 +22562,14 @@ Run /mcp reconnect in Claude Code to load the new version.`
|
|
|
22373
22562
|
}]
|
|
22374
22563
|
};
|
|
22375
22564
|
}
|
|
22376
|
-
var import_child_process7,
|
|
22565
|
+
var import_child_process7, import_fs51, import_path52;
|
|
22377
22566
|
var init_gossip_update = __esm({
|
|
22378
22567
|
"apps/cli/src/handlers/gossip-update.ts"() {
|
|
22379
22568
|
"use strict";
|
|
22380
22569
|
import_child_process7 = require("child_process");
|
|
22381
|
-
|
|
22382
|
-
|
|
22570
|
+
import_fs51 = require("fs");
|
|
22571
|
+
import_path52 = require("path");
|
|
22572
|
+
init_version();
|
|
22383
22573
|
}
|
|
22384
22574
|
});
|
|
22385
22575
|
|
|
@@ -22542,8 +22732,8 @@ var init_verify_memory = __esm({
|
|
|
22542
22732
|
});
|
|
22543
22733
|
|
|
22544
22734
|
// apps/cli/src/mcp-server-sdk.ts
|
|
22545
|
-
var
|
|
22546
|
-
var
|
|
22735
|
+
var import_fs52 = require("fs");
|
|
22736
|
+
var import_path53 = require("path");
|
|
22547
22737
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
22548
22738
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
22549
22739
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
@@ -36320,6 +36510,7 @@ config(en_default());
|
|
|
36320
36510
|
var import_crypto17 = require("crypto");
|
|
36321
36511
|
var import_http2 = require("http");
|
|
36322
36512
|
init_mcp_context();
|
|
36513
|
+
init_version();
|
|
36323
36514
|
|
|
36324
36515
|
// apps/cli/src/handlers/native-tasks.ts
|
|
36325
36516
|
var import_crypto10 = require("crypto");
|
|
@@ -36724,8 +36915,8 @@ ${utilityBlocks.join("\n\n")}`;
|
|
|
36724
36915
|
|
|
36725
36916
|
// apps/cli/src/handlers/dispatch.ts
|
|
36726
36917
|
var import_crypto11 = require("crypto");
|
|
36727
|
-
var
|
|
36728
|
-
var
|
|
36918
|
+
var import_fs32 = require("fs");
|
|
36919
|
+
var import_path34 = require("path");
|
|
36729
36920
|
init_src4();
|
|
36730
36921
|
init_src3();
|
|
36731
36922
|
init_mcp_context();
|
|
@@ -36842,7 +37033,7 @@ var AGENT_PROVIDER_MAP = {
|
|
|
36842
37033
|
};
|
|
36843
37034
|
function readQuotaState() {
|
|
36844
37035
|
try {
|
|
36845
|
-
const raw = (0,
|
|
37036
|
+
const raw = (0, import_fs32.readFileSync)((0, import_path34.join)(process.cwd(), ".gossip", "quota-state.json"), "utf8");
|
|
36846
37037
|
return JSON.parse(raw);
|
|
36847
37038
|
} catch {
|
|
36848
37039
|
return {};
|
|
@@ -37188,8 +37379,8 @@ async function handleDispatchConsensus(taskDefs, _utility_task_id) {
|
|
|
37188
37379
|
});
|
|
37189
37380
|
const lenses = await Promise.race([
|
|
37190
37381
|
lensPromise,
|
|
37191
|
-
new Promise((
|
|
37192
|
-
timerId = setTimeout(() =>
|
|
37382
|
+
new Promise((resolve18) => {
|
|
37383
|
+
timerId = setTimeout(() => resolve18(null), LENS_TIMEOUT_MS);
|
|
37193
37384
|
})
|
|
37194
37385
|
]);
|
|
37195
37386
|
if (timerId) clearTimeout(timerId);
|
|
@@ -37405,7 +37596,7 @@ Relay may be down. Check gossip_status() for connection state.` }] };
|
|
|
37405
37596
|
process.stderr.write(`[gossipcat] \u23F3 Consensus: ${doneCount}/${pendingNativeIds.length} agents complete (${agentStatus})
|
|
37406
37597
|
`);
|
|
37407
37598
|
}
|
|
37408
|
-
await new Promise((
|
|
37599
|
+
await new Promise((resolve18) => setTimeout(resolve18, POLL_INTERVAL));
|
|
37409
37600
|
}
|
|
37410
37601
|
const arrived = pendingNativeIds.filter((id) => ctx.nativeResultMap.has(id)).length;
|
|
37411
37602
|
const timedOutCount = pendingNativeIds.filter((id) => {
|
|
@@ -37902,12 +38093,83 @@ ${gaps.map((g) => ` - ${g.agentId} needs "${g.category}" (score: ${g.score.toFi
|
|
|
37902
38093
|
|
|
37903
38094
|
// apps/cli/src/mcp-server-sdk.ts
|
|
37904
38095
|
init_relay_cross_review();
|
|
37905
|
-
|
|
38096
|
+
|
|
38097
|
+
// apps/cli/src/stickyPort.ts
|
|
38098
|
+
var import_fs34 = require("fs");
|
|
38099
|
+
var import_path36 = require("path");
|
|
38100
|
+
var import_net = require("net");
|
|
38101
|
+
var RELAY_STICKY_FILE = (0, import_path36.join)(".gossip", "relay.port");
|
|
38102
|
+
var HTTP_MCP_STICKY_FILE = (0, import_path36.join)(".gossip", "http-mcp.port");
|
|
38103
|
+
function stickyPath(filename) {
|
|
38104
|
+
return (0, import_path36.join)(process.cwd(), filename);
|
|
38105
|
+
}
|
|
38106
|
+
function readStickyPort(filename) {
|
|
38107
|
+
const p = stickyPath(filename);
|
|
38108
|
+
if (!(0, import_fs34.existsSync)(p)) return null;
|
|
38109
|
+
try {
|
|
38110
|
+
const raw = (0, import_fs34.readFileSync)(p, "utf-8").trim();
|
|
38111
|
+
const n = parseInt(raw, 10);
|
|
38112
|
+
if (!Number.isFinite(n) || n < 1 || n > 65535) return null;
|
|
38113
|
+
return n;
|
|
38114
|
+
} catch {
|
|
38115
|
+
return null;
|
|
38116
|
+
}
|
|
38117
|
+
}
|
|
38118
|
+
function writeStickyPort(filename, port) {
|
|
38119
|
+
if (!Number.isFinite(port) || port < 1 || port > 65535) return;
|
|
38120
|
+
const p = stickyPath(filename);
|
|
38121
|
+
try {
|
|
38122
|
+
(0, import_fs34.mkdirSync)((0, import_path36.dirname)(p), { recursive: true });
|
|
38123
|
+
(0, import_fs34.writeFileSync)(p, String(port), "utf-8");
|
|
38124
|
+
} catch {
|
|
38125
|
+
}
|
|
38126
|
+
}
|
|
38127
|
+
function probePort(port, host = "127.0.0.1") {
|
|
38128
|
+
return new Promise((resolve18) => {
|
|
38129
|
+
const srv = (0, import_net.createServer)();
|
|
38130
|
+
let settled = false;
|
|
38131
|
+
const done = (ok) => {
|
|
38132
|
+
if (settled) return;
|
|
38133
|
+
settled = true;
|
|
38134
|
+
try {
|
|
38135
|
+
srv.close();
|
|
38136
|
+
} catch {
|
|
38137
|
+
}
|
|
38138
|
+
resolve18(ok);
|
|
38139
|
+
};
|
|
38140
|
+
srv.once("error", () => done(false));
|
|
38141
|
+
try {
|
|
38142
|
+
srv.listen(port, host, () => {
|
|
38143
|
+
srv.close(() => done(true));
|
|
38144
|
+
});
|
|
38145
|
+
} catch {
|
|
38146
|
+
done(false);
|
|
38147
|
+
}
|
|
38148
|
+
});
|
|
38149
|
+
}
|
|
38150
|
+
async function pickStickyPort(envVar, stickyFile, probeHost = "127.0.0.1") {
|
|
38151
|
+
const envRaw = process.env[envVar];
|
|
38152
|
+
if (envRaw !== void 0 && envRaw !== "") {
|
|
38153
|
+
const envPort = parseInt(envRaw, 10);
|
|
38154
|
+
if (Number.isFinite(envPort) && envPort >= 0 && envPort <= 65535) {
|
|
38155
|
+
return { port: envPort, source: "env" };
|
|
38156
|
+
}
|
|
38157
|
+
}
|
|
38158
|
+
const sticky = readStickyPort(stickyFile);
|
|
38159
|
+
if (sticky !== null) {
|
|
38160
|
+
const free = await probePort(sticky, probeHost);
|
|
38161
|
+
if (free) return { port: sticky, source: "sticky" };
|
|
38162
|
+
}
|
|
38163
|
+
return { port: 0, source: "auto" };
|
|
38164
|
+
}
|
|
38165
|
+
|
|
38166
|
+
// apps/cli/src/mcp-server-sdk.ts
|
|
38167
|
+
var gossipDir = (0, import_path53.join)(process.cwd(), ".gossip");
|
|
37906
38168
|
try {
|
|
37907
|
-
(0,
|
|
38169
|
+
(0, import_fs52.mkdirSync)(gossipDir, { recursive: true });
|
|
37908
38170
|
} catch {
|
|
37909
38171
|
}
|
|
37910
|
-
var logStream = (0,
|
|
38172
|
+
var logStream = (0, import_fs52.createWriteStream)((0, import_path53.join)(gossipDir, "mcp.log"), { flags: "a" });
|
|
37911
38173
|
process.stderr.write = ((chunk, ...args) => {
|
|
37912
38174
|
return logStream.write(chunk, ...args);
|
|
37913
38175
|
});
|
|
@@ -38071,17 +38333,18 @@ var booted = false;
|
|
|
38071
38333
|
var bootPromise = null;
|
|
38072
38334
|
var planExecutionDepth = 0;
|
|
38073
38335
|
var _pendingSessionData = /* @__PURE__ */ new Map();
|
|
38336
|
+
var _pendingSkillData = /* @__PURE__ */ new Map();
|
|
38074
38337
|
var _pendingVerifyData = /* @__PURE__ */ new Map();
|
|
38075
38338
|
var _modules = null;
|
|
38076
38339
|
function lookupFindingSeverity(findingId, projectRoot) {
|
|
38077
|
-
const { existsSync:
|
|
38078
|
-
const { join:
|
|
38079
|
-
const reportsDir =
|
|
38080
|
-
if (!
|
|
38340
|
+
const { existsSync: existsSync44, readdirSync: readdirSync13, readFileSync: readFileSync41 } = require("fs");
|
|
38341
|
+
const { join: join49 } = require("path");
|
|
38342
|
+
const reportsDir = join49(projectRoot, ".gossip", "consensus-reports");
|
|
38343
|
+
if (!existsSync44(reportsDir)) return null;
|
|
38081
38344
|
try {
|
|
38082
38345
|
const files = readdirSync13(reportsDir).filter((f) => f.endsWith(".json"));
|
|
38083
38346
|
for (const file2 of files) {
|
|
38084
|
-
const report = JSON.parse(
|
|
38347
|
+
const report = JSON.parse(readFileSync41(join49(reportsDir, file2), "utf-8"));
|
|
38085
38348
|
for (const bucket of ["confirmed", "disputed", "unverified", "unique"]) {
|
|
38086
38349
|
for (const finding of report[bucket] || []) {
|
|
38087
38350
|
if (finding.id === findingId && finding.severity) {
|
|
@@ -38144,12 +38407,21 @@ async function refreshBootstrap() {
|
|
|
38144
38407
|
async function doBoot() {
|
|
38145
38408
|
const m = await getModules();
|
|
38146
38409
|
const configPath = m.findConfigPath();
|
|
38147
|
-
|
|
38148
|
-
|
|
38410
|
+
let config2;
|
|
38411
|
+
if (configPath) {
|
|
38412
|
+
config2 = m.loadConfig(configPath);
|
|
38413
|
+
} else {
|
|
38414
|
+
process.stderr.write("[gossipcat] \u26A0\uFE0F No gossip.agents.json found \u2014 booting in degraded mode (dashboard + relay only). Run gossip_setup inside Claude Code to create your agent team.\n");
|
|
38415
|
+
config2 = {
|
|
38416
|
+
main_agent: { provider: "none", model: "none" },
|
|
38417
|
+
utility_model: { provider: "none", model: "none" },
|
|
38418
|
+
agents: {}
|
|
38419
|
+
};
|
|
38420
|
+
}
|
|
38149
38421
|
const agentConfigs = m.configToAgentConfigs(config2);
|
|
38150
38422
|
ctx.keychain = new m.Keychain();
|
|
38151
38423
|
const { existsSync: pidExists, readFileSync: readPid, writeFileSync: writePid, unlinkSync: delPid } = require("fs");
|
|
38152
|
-
const pidFile = (0,
|
|
38424
|
+
const pidFile = (0, import_path53.join)(process.cwd(), ".gossip", "relay.pid");
|
|
38153
38425
|
if (pidExists(pidFile)) {
|
|
38154
38426
|
const oldPid = parseInt(readPid(pidFile, "utf-8").trim(), 10);
|
|
38155
38427
|
if (!isNaN(oldPid) && oldPid !== process.pid) {
|
|
@@ -38162,8 +38434,11 @@ async function doBoot() {
|
|
|
38162
38434
|
}
|
|
38163
38435
|
}
|
|
38164
38436
|
const relayApiKey = (0, import_crypto17.randomBytes)(32).toString("hex");
|
|
38437
|
+
const relayPick = await pickStickyPort("GOSSIPCAT_PORT", RELAY_STICKY_FILE);
|
|
38438
|
+
const relayPort = relayPick.port;
|
|
38439
|
+
ctx.relayPortSource = relayPick.source;
|
|
38165
38440
|
ctx.relay = new m.RelayServer({
|
|
38166
|
-
port:
|
|
38441
|
+
port: relayPort,
|
|
38167
38442
|
apiKey: relayApiKey,
|
|
38168
38443
|
dashboard: {
|
|
38169
38444
|
projectRoot: process.cwd(),
|
|
@@ -38171,7 +38446,10 @@ async function doBoot() {
|
|
|
38171
38446
|
}
|
|
38172
38447
|
});
|
|
38173
38448
|
await ctx.relay.start();
|
|
38174
|
-
|
|
38449
|
+
if (typeof ctx.relay.port === "number" && ctx.relay.port > 0) {
|
|
38450
|
+
writeStickyPort(RELAY_STICKY_FILE, ctx.relay.port);
|
|
38451
|
+
}
|
|
38452
|
+
await startHttpMcpTransport();
|
|
38175
38453
|
try {
|
|
38176
38454
|
writePid(pidFile, String(process.pid));
|
|
38177
38455
|
} catch {
|
|
@@ -38237,10 +38515,10 @@ async function doBoot() {
|
|
|
38237
38515
|
}
|
|
38238
38516
|
const key = await ctx.keychain.getKey(ac.provider);
|
|
38239
38517
|
const llm = m.createProvider(ac.provider, ac.model, key ?? void 0, void 0, ac.base_url);
|
|
38240
|
-
const { existsSync:
|
|
38241
|
-
const { join:
|
|
38242
|
-
const instructionsPath =
|
|
38243
|
-
const baseInstructions =
|
|
38518
|
+
const { existsSync: existsSync44, readFileSync: readFileSync41 } = require("fs");
|
|
38519
|
+
const { join: join49 } = require("path");
|
|
38520
|
+
const instructionsPath = join49(process.cwd(), ".gossip", "agents", ac.id, "instructions.md");
|
|
38521
|
+
const baseInstructions = existsSync44(instructionsPath) ? readFileSync41(instructionsPath, "utf-8") : "";
|
|
38244
38522
|
const identity = identityRegistry.get(ac.id);
|
|
38245
38523
|
const identityBlock = identity ? m.formatIdentityBlock(identity) + "\n" : "";
|
|
38246
38524
|
const instructions = (identityBlock + baseInstructions).trim() || void 0;
|
|
@@ -38298,8 +38576,13 @@ async function doBoot() {
|
|
|
38298
38576
|
if (!mainKey) {
|
|
38299
38577
|
mainProvider = "none";
|
|
38300
38578
|
config2.main_agent.provider = "none";
|
|
38301
|
-
|
|
38579
|
+
if (env.host === "claude-code") {
|
|
38580
|
+
process.stderr.write(`[gossipcat] \u2705 Native Claude Code orchestration enabled (no API LLM needed \u2014 host classifies via natural language)
|
|
38581
|
+
`);
|
|
38582
|
+
} else {
|
|
38583
|
+
process.stderr.write(`[gossipcat] \u274C No API keys available \u2014 orchestrator LLM disabled, features degrade to profile-based
|
|
38302
38584
|
`);
|
|
38585
|
+
}
|
|
38303
38586
|
}
|
|
38304
38587
|
}
|
|
38305
38588
|
ctx.mainProvider = mainProvider;
|
|
@@ -38572,7 +38855,7 @@ ctx.getModules = getModules;
|
|
|
38572
38855
|
var server = new import_mcp.McpServer(
|
|
38573
38856
|
{
|
|
38574
38857
|
name: "gossipcat",
|
|
38575
|
-
version:
|
|
38858
|
+
version: getGossipcatVersion()
|
|
38576
38859
|
},
|
|
38577
38860
|
{
|
|
38578
38861
|
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."
|
|
@@ -38796,18 +39079,21 @@ server.tool(
|
|
|
38796
39079
|
"Status:",
|
|
38797
39080
|
` Host: ${env.host}${env.supportsNativeAgents ? " (native agents supported)" : ""}`,
|
|
38798
39081
|
` Native agent dir: ${env.nativeAgentDir || "n/a"}`,
|
|
38799
|
-
` Relay: ${ctx.relay ? `running :${ctx.relay.port}` : "not started"}`,
|
|
39082
|
+
` Relay: ${ctx.relay ? `running :${ctx.relay.port}${ctx.relayPortSource === "sticky" ? " (sticky)" : ""}` : "not started"}`,
|
|
38800
39083
|
` Tool Server: ${ctx.toolServer ? "running" : "not started"}`,
|
|
38801
39084
|
` Workers: ${ctx.workers.size} (${Array.from(ctx.workers.keys()).join(", ") || "none"})`,
|
|
38802
39085
|
` Claude subagents found: ${claudeSubagentsList.length}`
|
|
38803
39086
|
];
|
|
38804
39087
|
if (ctx.relay?.dashboardUrl) {
|
|
38805
|
-
lines.push(` Dashboard: ${ctx.relay.dashboardUrl} (key: ${ctx.relay.dashboardKey})`);
|
|
39088
|
+
lines.push(` Dashboard: ${ctx.relay.dashboardUrl}${ctx.relayPortSource === "sticky" ? " (sticky)" : ""} (key: ${ctx.relay.dashboardKey})`);
|
|
39089
|
+
}
|
|
39090
|
+
if (ctx.httpMcpPort) {
|
|
39091
|
+
lines.push(` HTTP MCP: :${ctx.httpMcpPort}/mcp${ctx.httpMcpPortSource === "sticky" ? " (sticky)" : ""}`);
|
|
38806
39092
|
}
|
|
38807
39093
|
try {
|
|
38808
|
-
const { readFileSync:
|
|
38809
|
-
const quotaPath = (0,
|
|
38810
|
-
const quotaRaw =
|
|
39094
|
+
const { readFileSync: readFileSync41 } = await import("fs");
|
|
39095
|
+
const quotaPath = (0, import_path53.join)(process.cwd(), ".gossip", "quota-state.json");
|
|
39096
|
+
const quotaRaw = readFileSync41(quotaPath, "utf8");
|
|
38811
39097
|
const quotaState = JSON.parse(quotaRaw);
|
|
38812
39098
|
for (const [provider, state] of Object.entries(quotaState)) {
|
|
38813
39099
|
const now = Date.now();
|
|
@@ -38821,16 +39107,16 @@ server.tool(
|
|
|
38821
39107
|
} catch {
|
|
38822
39108
|
}
|
|
38823
39109
|
try {
|
|
38824
|
-
const { readFileSync:
|
|
38825
|
-
const reportsDir = (0,
|
|
38826
|
-
const perfPath = (0,
|
|
39110
|
+
const { readFileSync: readFileSync41, readdirSync: readdirSync13, statSync: statSync9 } = await import("fs");
|
|
39111
|
+
const reportsDir = (0, import_path53.join)(process.cwd(), ".gossip", "consensus-reports");
|
|
39112
|
+
const perfPath = (0, import_path53.join)(process.cwd(), ".gossip", "agent-performance.jsonl");
|
|
38827
39113
|
const WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
38828
39114
|
const now = Date.now();
|
|
38829
39115
|
const recentReports = [];
|
|
38830
39116
|
try {
|
|
38831
39117
|
for (const fname of readdirSync13(reportsDir)) {
|
|
38832
39118
|
if (!fname.endsWith(".json")) continue;
|
|
38833
|
-
const fpath = (0,
|
|
39119
|
+
const fpath = (0, import_path53.join)(reportsDir, fname);
|
|
38834
39120
|
const st = statSync9(fpath);
|
|
38835
39121
|
if (now - st.mtimeMs > WINDOW_MS) continue;
|
|
38836
39122
|
recentReports.push({ id: fname.replace(/\.json$/, ""), mtimeMs: st.mtimeMs });
|
|
@@ -38840,7 +39126,7 @@ server.tool(
|
|
|
38840
39126
|
if (recentReports.length > 0) {
|
|
38841
39127
|
const covered = /* @__PURE__ */ new Set();
|
|
38842
39128
|
try {
|
|
38843
|
-
const perfRaw =
|
|
39129
|
+
const perfRaw = readFileSync41(perfPath, "utf8");
|
|
38844
39130
|
for (const line of perfRaw.split("\n")) {
|
|
38845
39131
|
if (!line) continue;
|
|
38846
39132
|
try {
|
|
@@ -38972,7 +39258,73 @@ ${toolsMatch[1].trim()}`;
|
|
|
38972
39258
|
}
|
|
38973
39259
|
} catch {
|
|
38974
39260
|
}
|
|
38975
|
-
|
|
39261
|
+
let handbookSection = "";
|
|
39262
|
+
try {
|
|
39263
|
+
const { readFileSync: rfHB, statSync: stHB } = require("fs");
|
|
39264
|
+
const { join: jHB } = require("path");
|
|
39265
|
+
const handbookPath = jHB(process.cwd(), "docs", "HANDBOOK.md");
|
|
39266
|
+
const stat3 = stHB(handbookPath);
|
|
39267
|
+
const HANDBOOK_CAP_BYTES = 24 * 1024;
|
|
39268
|
+
let body = rfHB(handbookPath, "utf-8");
|
|
39269
|
+
const truncated = body.length > HANDBOOK_CAP_BYTES;
|
|
39270
|
+
if (truncated) {
|
|
39271
|
+
body = body.slice(0, HANDBOOK_CAP_BYTES);
|
|
39272
|
+
}
|
|
39273
|
+
handbookSection = "\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n## Project Handbook (auto-loaded from docs/HANDBOOK.md)\n\n" + body.trim() + (truncated ? `
|
|
39274
|
+
|
|
39275
|
+
[handbook truncated at ${HANDBOOK_CAP_BYTES / 1024}KB \u2014 full file at docs/HANDBOOK.md, ${stat3.size} bytes total]` : "");
|
|
39276
|
+
} catch {
|
|
39277
|
+
}
|
|
39278
|
+
return { content: [{ type: "text", text: lines.join("\n") + "\n\n" + agentSections.join("\n") + sessionContextSection + handbookSection }] };
|
|
39279
|
+
}
|
|
39280
|
+
);
|
|
39281
|
+
server.tool(
|
|
39282
|
+
"gossip_guide",
|
|
39283
|
+
"Show the gossipcat handbook for humans \u2014 architectural invariants, operator playbook, caveats, hallucination patterns, glossary. Call this when you want to READ the docs, not when you need LLM context. The LLM-facing auto-load lives in gossip_status().",
|
|
39284
|
+
{},
|
|
39285
|
+
async () => {
|
|
39286
|
+
try {
|
|
39287
|
+
const { readFileSync: rfG, existsSync: exG } = require("fs");
|
|
39288
|
+
const { join: jG, dirname: dG } = require("path");
|
|
39289
|
+
const projectHandbook = jG(process.cwd(), "docs", "HANDBOOK.md");
|
|
39290
|
+
if (exG(projectHandbook)) {
|
|
39291
|
+
const body = rfG(projectHandbook, "utf-8");
|
|
39292
|
+
return {
|
|
39293
|
+
content: [{
|
|
39294
|
+
type: "text",
|
|
39295
|
+
text: `# Gossipcat Handbook (from ${projectHandbook})
|
|
39296
|
+
|
|
39297
|
+
${body}`
|
|
39298
|
+
}]
|
|
39299
|
+
};
|
|
39300
|
+
}
|
|
39301
|
+
const bundleRoot = dG(require.resolve("../../dist-mcp/mcp-server.js").replace(/\/dist-mcp\/mcp-server\.js$/, "/dist-mcp/mcp-server.js"));
|
|
39302
|
+
const defaultHandbook = jG(bundleRoot, "..", "docs", "HANDBOOK.md");
|
|
39303
|
+
if (exG(defaultHandbook)) {
|
|
39304
|
+
const body = rfG(defaultHandbook, "utf-8");
|
|
39305
|
+
return {
|
|
39306
|
+
content: [{
|
|
39307
|
+
type: "text",
|
|
39308
|
+
text: `# Gossipcat Handbook (bundled default \u2014 your project has no docs/HANDBOOK.md yet)
|
|
39309
|
+
|
|
39310
|
+
${body}`
|
|
39311
|
+
}]
|
|
39312
|
+
};
|
|
39313
|
+
}
|
|
39314
|
+
return {
|
|
39315
|
+
content: [{
|
|
39316
|
+
type: "text",
|
|
39317
|
+
text: "No handbook found. Expected at docs/HANDBOOK.md in the current project or bundled with gossipcat. If you are inside a gossipcat project, run `gossip_setup` first."
|
|
39318
|
+
}]
|
|
39319
|
+
};
|
|
39320
|
+
} catch (err) {
|
|
39321
|
+
return {
|
|
39322
|
+
content: [{
|
|
39323
|
+
type: "text",
|
|
39324
|
+
text: `Error reading handbook: ${err.message}`
|
|
39325
|
+
}]
|
|
39326
|
+
};
|
|
39327
|
+
}
|
|
38976
39328
|
}
|
|
38977
39329
|
);
|
|
38978
39330
|
server.tool(
|
|
@@ -39079,8 +39431,8 @@ server.tool(
|
|
|
39079
39431
|
}
|
|
39080
39432
|
return { content: [{ type: "text", text: results.join("\n") }] };
|
|
39081
39433
|
}
|
|
39082
|
-
const { writeFileSync:
|
|
39083
|
-
const { join:
|
|
39434
|
+
const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22, existsSync: existsSync44 } = require("fs");
|
|
39435
|
+
const { join: join49 } = require("path");
|
|
39084
39436
|
const root = process.cwd();
|
|
39085
39437
|
const CLAUDE_MODEL_MAP2 = {
|
|
39086
39438
|
opus: { provider: "anthropic", model: "claude-opus-4-6" },
|
|
@@ -39094,8 +39446,8 @@ server.tool(
|
|
|
39094
39446
|
let existingAgents = {};
|
|
39095
39447
|
if (mode === "merge") {
|
|
39096
39448
|
try {
|
|
39097
|
-
const { readFileSync:
|
|
39098
|
-
const existing = JSON.parse(
|
|
39449
|
+
const { readFileSync: readFileSync41 } = require("fs");
|
|
39450
|
+
const existing = JSON.parse(readFileSync41(join49(root, ".gossip", "config.json"), "utf-8"));
|
|
39099
39451
|
existingAgents = existing.agents || {};
|
|
39100
39452
|
} catch {
|
|
39101
39453
|
}
|
|
@@ -39127,9 +39479,9 @@ server.tool(
|
|
|
39127
39479
|
"",
|
|
39128
39480
|
body
|
|
39129
39481
|
].join("\n");
|
|
39130
|
-
const agentsDir =
|
|
39131
|
-
|
|
39132
|
-
|
|
39482
|
+
const agentsDir = join49(root, ".claude", "agents");
|
|
39483
|
+
mkdirSync22(agentsDir, { recursive: true });
|
|
39484
|
+
writeFileSync18(join49(agentsDir, `${agent.id}.md`), md, "utf-8");
|
|
39133
39485
|
nativeCreated.push(agent.id);
|
|
39134
39486
|
configAgents[agent.id] = {
|
|
39135
39487
|
provider: mapped.provider,
|
|
@@ -39147,8 +39499,8 @@ server.tool(
|
|
|
39147
39499
|
errors.push(`${agent.id}: custom agent requires "custom_model" field`);
|
|
39148
39500
|
continue;
|
|
39149
39501
|
}
|
|
39150
|
-
const nativeFile =
|
|
39151
|
-
const wasNative = existingAgents[agent.id]?.native ||
|
|
39502
|
+
const nativeFile = join49(root, ".claude", "agents", `${agent.id}.md`);
|
|
39503
|
+
const wasNative = existingAgents[agent.id]?.native || existsSync44(nativeFile);
|
|
39152
39504
|
if (wasNative) {
|
|
39153
39505
|
errors.push(`${agent.id}: cannot re-register native agent as custom \u2014 .claude/agents/${agent.id}.md exists. Remove the file first or keep it as native.`);
|
|
39154
39506
|
continue;
|
|
@@ -39162,9 +39514,9 @@ server.tool(
|
|
|
39162
39514
|
};
|
|
39163
39515
|
customCreated.push(agent.id);
|
|
39164
39516
|
if (agent.instructions) {
|
|
39165
|
-
const instrDir =
|
|
39166
|
-
|
|
39167
|
-
|
|
39517
|
+
const instrDir = join49(root, ".gossip", "agents", agent.id);
|
|
39518
|
+
mkdirSync22(instrDir, { recursive: true });
|
|
39519
|
+
writeFileSync18(join49(instrDir, "instructions.md"), agent.instructions, "utf-8");
|
|
39168
39520
|
}
|
|
39169
39521
|
}
|
|
39170
39522
|
}
|
|
@@ -39178,13 +39530,13 @@ server.tool(
|
|
|
39178
39530
|
} catch (err) {
|
|
39179
39531
|
return { content: [{ type: "text", text: `Invalid config: ${err.message}` }] };
|
|
39180
39532
|
}
|
|
39181
|
-
|
|
39182
|
-
|
|
39533
|
+
mkdirSync22(join49(root, ".gossip"), { recursive: true });
|
|
39534
|
+
writeFileSync18(join49(root, ".gossip", "config.json"), JSON.stringify(config2, null, 2));
|
|
39183
39535
|
const agentList = Object.entries(config2.agents).map(([id, a]) => `- ${id}: ${a.provider}/${a.model} (${a.preset || "custom"})${a.native ? " \u2014 native" : ""}`).join("\n");
|
|
39184
|
-
const rulesDir =
|
|
39185
|
-
const rulesFile =
|
|
39186
|
-
|
|
39187
|
-
|
|
39536
|
+
const rulesDir = join49(root, env.rulesDir);
|
|
39537
|
+
const rulesFile = join49(root, env.rulesFile);
|
|
39538
|
+
mkdirSync22(rulesDir, { recursive: true });
|
|
39539
|
+
writeFileSync18(rulesFile, generateRulesContent(agentList));
|
|
39188
39540
|
const lines = [`Host: ${env.host}`, ""];
|
|
39189
39541
|
if (nativeCreated.length > 0) {
|
|
39190
39542
|
lines.push(`Native agents created (${nativeCreated.length}):`);
|
|
@@ -39890,9 +40242,11 @@ server.tool(
|
|
|
39890
40242
|
skills: external_exports.array(external_exports.object({
|
|
39891
40243
|
name: external_exports.string().describe("Skill name (kebab-case)"),
|
|
39892
40244
|
content: external_exports.string().describe("Full .md content with frontmatter")
|
|
39893
|
-
})).optional().describe("For build: generated skill files to save. Omit for discovery mode.")
|
|
40245
|
+
})).optional().describe("For build: generated skill files to save. Omit for discovery mode."),
|
|
40246
|
+
// Internal: re-entry param for native-utility dispatch path (develop action only)
|
|
40247
|
+
_utility_task_id: external_exports.string().optional().describe("Internal: utility task ID for re-entry after native skill generation")
|
|
39894
40248
|
},
|
|
39895
|
-
async ({ action, agent_id, skill, enabled, category, skill_names, skills }) => {
|
|
40249
|
+
async ({ action, agent_id, skill, enabled, category, skill_names, skills, _utility_task_id }) => {
|
|
39896
40250
|
await boot();
|
|
39897
40251
|
if (action === "list") {
|
|
39898
40252
|
const index = ctx.mainAgent.getSkillIndex();
|
|
@@ -39948,6 +40302,98 @@ ${sections.join("\n\n")}` }] };
|
|
|
39948
40302
|
if (!ctx.skillEngine) {
|
|
39949
40303
|
return { content: [{ type: "text", text: "Skill engine not available. Check boot logs." }] };
|
|
39950
40304
|
}
|
|
40305
|
+
if (_utility_task_id) {
|
|
40306
|
+
const stashedMeta = _pendingSkillData.get(_utility_task_id);
|
|
40307
|
+
_pendingSkillData.delete(_utility_task_id);
|
|
40308
|
+
const utilityResult = ctx.nativeResultMap.get(_utility_task_id);
|
|
40309
|
+
ctx.nativeResultMap.delete(_utility_task_id);
|
|
40310
|
+
ctx.nativeTaskMap.delete(_utility_task_id);
|
|
40311
|
+
if (utilityResult?.status === "completed" && utilityResult.result && stashedMeta) {
|
|
40312
|
+
try {
|
|
40313
|
+
const result = ctx.skillEngine.saveFromRaw(agent_id, category, utilityResult.result, stashedMeta);
|
|
40314
|
+
const { normalizeSkillName: nsn } = await Promise.resolve().then(() => (init_src4(), src_exports3));
|
|
40315
|
+
const skillName = nsn(category);
|
|
40316
|
+
if (ctx.mainAgent) {
|
|
40317
|
+
const skillIndex = ctx.mainAgent.getSkillIndex();
|
|
40318
|
+
if (skillIndex) {
|
|
40319
|
+
skillIndex.bind(agent_id, skillName, { source: "auto", mode: "permanent" });
|
|
40320
|
+
}
|
|
40321
|
+
const registry2 = ctx.mainAgent.registry;
|
|
40322
|
+
const agentConfig = registry2?.get(agent_id);
|
|
40323
|
+
const normalizedCategory = nsn(category);
|
|
40324
|
+
if (agentConfig && !agentConfig.skills.includes(normalizedCategory)) {
|
|
40325
|
+
agentConfig.skills.push(normalizedCategory);
|
|
40326
|
+
}
|
|
40327
|
+
const pipeline = ctx.mainAgent.pipeline;
|
|
40328
|
+
if (pipeline?.suppressSkillGapAlert) {
|
|
40329
|
+
pipeline.suppressSkillGapAlert(agent_id, category);
|
|
40330
|
+
}
|
|
40331
|
+
}
|
|
40332
|
+
const preview = result.content.length > 1e3 ? result.content.slice(0, 1e3) + "\n\n... (truncated)" : result.content;
|
|
40333
|
+
process.stderr.write(`[gossipcat] Skill developed (native): "${skillName}" for ${agent_id} (category: ${category})
|
|
40334
|
+
`);
|
|
40335
|
+
return {
|
|
40336
|
+
content: [{ type: "text", text: `Skill generated and saved:
|
|
40337
|
+
|
|
40338
|
+
Path: ${result.path}
|
|
40339
|
+
|
|
40340
|
+
Auto-bound "${skillName}" to ${agent_id} in skill index.
|
|
40341
|
+
|
|
40342
|
+
${preview}` }]
|
|
40343
|
+
};
|
|
40344
|
+
} catch (err) {
|
|
40345
|
+
process.stderr.write(`[gossipcat] Skill develop native post-processing failed: ${err.message}
|
|
40346
|
+
`);
|
|
40347
|
+
return {
|
|
40348
|
+
content: [{ type: "text", text: `Skill generation failed: ${err.message}` }]
|
|
40349
|
+
};
|
|
40350
|
+
}
|
|
40351
|
+
} else {
|
|
40352
|
+
process.stderr.write(`[gossipcat] Skill develop utility ${_utility_task_id} failed/missing, falling back to direct LLM
|
|
40353
|
+
`);
|
|
40354
|
+
}
|
|
40355
|
+
}
|
|
40356
|
+
if (ctx.nativeUtilityConfig && !_utility_task_id) {
|
|
40357
|
+
try {
|
|
40358
|
+
const { system, user, skillName, skillPath, baseline_accuracy_correct, baseline_accuracy_hallucinated, bound_at } = await ctx.skillEngine.buildPrompt(agent_id, category);
|
|
40359
|
+
const taskId = (0, import_crypto17.randomUUID)().slice(0, 8);
|
|
40360
|
+
_pendingSkillData.set(taskId, { agentId: agent_id, category, skillName, skillPath, baseline_accuracy_correct, baseline_accuracy_hallucinated, bound_at });
|
|
40361
|
+
ctx.nativeTaskMap.set(taskId, {
|
|
40362
|
+
agentId: "_utility",
|
|
40363
|
+
task: `skill_develop:${category}`,
|
|
40364
|
+
startedAt: Date.now(),
|
|
40365
|
+
timeoutMs: 12e4,
|
|
40366
|
+
utilityType: "skill_develop"
|
|
40367
|
+
});
|
|
40368
|
+
spawnTimeoutWatcher(taskId, ctx.nativeTaskMap.get(taskId));
|
|
40369
|
+
const modelShort = ctx.nativeUtilityConfig.model;
|
|
40370
|
+
return {
|
|
40371
|
+
content: [
|
|
40372
|
+
{
|
|
40373
|
+
type: "text",
|
|
40374
|
+
text: `Skill prompt built. Dispatching native utility for generation.
|
|
40375
|
+
|
|
40376
|
+
\u26A0\uFE0F EXECUTE NOW \u2014 launch this Agent and re-call gossip_skills:
|
|
40377
|
+
|
|
40378
|
+
1. Agent(model: "${modelShort}", prompt: <AGENT_PROMPT:${taskId} below>, run_in_background: true) \u2014 pass the AGENT_PROMPT:${taskId} content item verbatim
|
|
40379
|
+
2. When agent completes \u2192 gossip_relay(task_id: "${taskId}", result: "<full agent output>")
|
|
40380
|
+
3. Then re-call: gossip_skills(action: "develop", agent_id: "${agent_id}", category: "${category}", _utility_task_id: "${taskId}")
|
|
40381
|
+
|
|
40382
|
+
Do ALL steps in order. Do not wait for user input between them.`
|
|
40383
|
+
},
|
|
40384
|
+
{ type: "text", text: `AGENT_PROMPT:${taskId} (_utility)
|
|
40385
|
+
${system}
|
|
40386
|
+
|
|
40387
|
+
---
|
|
40388
|
+
|
|
40389
|
+
${user}` }
|
|
40390
|
+
]
|
|
40391
|
+
};
|
|
40392
|
+
} catch (err) {
|
|
40393
|
+
process.stderr.write(`[gossipcat] Skill develop native prompt build failed: ${err.message}, falling back to direct LLM
|
|
40394
|
+
`);
|
|
40395
|
+
}
|
|
40396
|
+
}
|
|
39951
40397
|
try {
|
|
39952
40398
|
const result = await ctx.skillEngine.generate(agent_id, category);
|
|
39953
40399
|
const { normalizeSkillName: nsn } = await Promise.resolve().then(() => (init_src4(), src_exports3));
|
|
@@ -39991,16 +40437,16 @@ ${preview}` }]
|
|
|
39991
40437
|
const { SkillGapTracker: SkillGapTracker2, parseSkillFrontmatter: parseSkillFrontmatter2, normalizeSkillName: normalizeSkillName2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
|
|
39992
40438
|
const tracker = new SkillGapTracker2(process.cwd());
|
|
39993
40439
|
if (skills && skills.length > 0) {
|
|
39994
|
-
const { writeFileSync:
|
|
39995
|
-
const { join:
|
|
39996
|
-
const dir =
|
|
39997
|
-
|
|
40440
|
+
const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22, existsSync: existsSync44, readFileSync: readFileSync41 } = require("fs");
|
|
40441
|
+
const { join: join49 } = require("path");
|
|
40442
|
+
const dir = join49(process.cwd(), ".gossip", "skills");
|
|
40443
|
+
mkdirSync22(dir, { recursive: true });
|
|
39998
40444
|
const results = [];
|
|
39999
40445
|
for (const sk of skills) {
|
|
40000
40446
|
const name = normalizeSkillName2(sk.name);
|
|
40001
|
-
const filePath =
|
|
40002
|
-
if (
|
|
40003
|
-
const existing =
|
|
40447
|
+
const filePath = join49(dir, `${name}.md`);
|
|
40448
|
+
if (existsSync44(filePath)) {
|
|
40449
|
+
const existing = readFileSync41(filePath, "utf-8");
|
|
40004
40450
|
const fm = parseSkillFrontmatter2(existing);
|
|
40005
40451
|
if (fm) {
|
|
40006
40452
|
if (fm.generated_by === "manual") {
|
|
@@ -40017,7 +40463,7 @@ ${preview}` }]
|
|
|
40017
40463
|
}
|
|
40018
40464
|
}
|
|
40019
40465
|
}
|
|
40020
|
-
|
|
40466
|
+
writeFileSync18(filePath, sk.content);
|
|
40021
40467
|
tracker.recordResolution(name);
|
|
40022
40468
|
results.push(`Created .gossip/skills/${name}.md`);
|
|
40023
40469
|
}
|
|
@@ -40061,9 +40507,10 @@ server.tool(
|
|
|
40061
40507
|
"Save a cognitive session summary to project memory. The next session will load this context automatically on MCP connect. Call before ending your session to preserve what was learned.",
|
|
40062
40508
|
{
|
|
40063
40509
|
notes: external_exports.string().optional().describe('Optional freeform user context (e.g., "focusing on security hardening")'),
|
|
40510
|
+
force: external_exports.boolean().optional().describe("Bypass the refuse-gate for pending native tasks / consensus rounds. Audited to .gossip/forced-saves.jsonl."),
|
|
40064
40511
|
_utility_task_id: external_exports.string().optional().describe("Internal: utility task ID for re-entry after native session summary")
|
|
40065
40512
|
},
|
|
40066
|
-
async ({ notes, _utility_task_id }) => {
|
|
40513
|
+
async ({ notes, force, _utility_task_id }) => {
|
|
40067
40514
|
await boot();
|
|
40068
40515
|
if (_utility_task_id) {
|
|
40069
40516
|
const stashed = _pendingSessionData.get(_utility_task_id);
|
|
@@ -40164,6 +40611,48 @@ server.tool(
|
|
|
40164
40611
|
|
|
40165
40612
|
${summary2}` }] };
|
|
40166
40613
|
}
|
|
40614
|
+
{
|
|
40615
|
+
const pendingNative = [];
|
|
40616
|
+
for (const [taskId, info] of ctx.nativeTaskMap) {
|
|
40617
|
+
if (!ctx.nativeResultMap.has(taskId)) pendingNative.push(`${taskId} (${info.agentId})`);
|
|
40618
|
+
}
|
|
40619
|
+
const pendingConsensus = [];
|
|
40620
|
+
const now = Date.now();
|
|
40621
|
+
for (const [cid, round] of ctx.pendingConsensusRounds) {
|
|
40622
|
+
if (round.deadline && now > round.deadline) continue;
|
|
40623
|
+
if (round.pendingNativeAgents && round.pendingNativeAgents.size > 0) {
|
|
40624
|
+
pendingConsensus.push(`${cid} (${round.pendingNativeAgents.size} agents)`);
|
|
40625
|
+
}
|
|
40626
|
+
}
|
|
40627
|
+
if ((pendingNative.length > 0 || pendingConsensus.length > 0) && !force) {
|
|
40628
|
+
const lines = ["\u26D4 session_save refused \u2014 work still in flight.\n"];
|
|
40629
|
+
if (pendingNative.length > 0) {
|
|
40630
|
+
lines.push(`Pending native tasks (${pendingNative.length}):`);
|
|
40631
|
+
for (const t of pendingNative.slice(0, 10)) lines.push(` - ${t}`);
|
|
40632
|
+
lines.push("");
|
|
40633
|
+
}
|
|
40634
|
+
if (pendingConsensus.length > 0) {
|
|
40635
|
+
lines.push(`Pending consensus rounds (${pendingConsensus.length}):`);
|
|
40636
|
+
for (const c of pendingConsensus.slice(0, 10)) lines.push(` - ${c}`);
|
|
40637
|
+
lines.push("");
|
|
40638
|
+
}
|
|
40639
|
+
lines.push("Relay the results via gossip_relay / gossip_relay_cross_review, then re-call gossip_session_save.");
|
|
40640
|
+
lines.push("To override (snapshot is intentional / tasks are abandoned): gossip_session_save(force: true).");
|
|
40641
|
+
return { content: [{ type: "text", text: lines.join("\n") }], isError: true };
|
|
40642
|
+
}
|
|
40643
|
+
if (force && (pendingNative.length > 0 || pendingConsensus.length > 0)) {
|
|
40644
|
+
try {
|
|
40645
|
+
const { appendFileSync: af, mkdirSync: md } = require("fs");
|
|
40646
|
+
const { join: j } = require("path");
|
|
40647
|
+
md(j(process.cwd(), ".gossip"), { recursive: true });
|
|
40648
|
+
af(
|
|
40649
|
+
j(process.cwd(), ".gossip", "forced-saves.jsonl"),
|
|
40650
|
+
JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), pendingNative, pendingConsensus, notes: notes || null }) + "\n"
|
|
40651
|
+
);
|
|
40652
|
+
} catch {
|
|
40653
|
+
}
|
|
40654
|
+
}
|
|
40655
|
+
}
|
|
40167
40656
|
let gossipText = "";
|
|
40168
40657
|
try {
|
|
40169
40658
|
const { existsSync: ex, readFileSync: rf } = require("fs");
|
|
@@ -40533,6 +41022,7 @@ server.tool(
|
|
|
40533
41022
|
{ name: "gossip_remember", desc: "Search an agent's archived knowledge files by keyword query." },
|
|
40534
41023
|
{ name: "gossip_verify_memory", desc: "On-demand staleness check for a memory file claim. Returns FRESH | STALE | CONTRADICTED | INCONCLUSIVE with file:line evidence." },
|
|
40535
41024
|
{ name: "gossip_tools", desc: "List available tools (this command)." },
|
|
41025
|
+
{ name: "gossip_guide", desc: "Show the gossipcat handbook for humans \u2014 invariants, operator playbook, caveats, hallucination patterns, glossary. Read the docs, not LLM context." },
|
|
40536
41026
|
{ name: "gossip_progress", desc: "Show active task progress and consensus phase. No params." },
|
|
40537
41027
|
{ name: "gossip_format", desc: "Return the CONSENSUS_OUTPUT_FORMAT block to paste into ad-hoc Agent() prompts so native subagents emit parseable <agent_finding> tags." },
|
|
40538
41028
|
{ name: "gossip_bug_feedback", desc: "File a GitHub issue on the gossipcat repo from an in-session bug report. Dedupes against open issues." }
|
|
@@ -40685,14 +41175,8 @@ ${match.url}` }] };
|
|
|
40685
41175
|
}
|
|
40686
41176
|
return { content: [{ type: "text", text: `Dedup check failed (${err.message ?? "unknown error"}). Not filing to avoid duplicates. Run \`gh auth status\` and retry.` }] };
|
|
40687
41177
|
}
|
|
40688
|
-
|
|
40689
|
-
|
|
40690
|
-
const { readFileSync: readFileSync40 } = await import("fs");
|
|
40691
|
-
const { join: join48 } = await import("path");
|
|
40692
|
-
const pkgRaw = readFileSync40(join48(process.cwd(), "package.json"), "utf-8");
|
|
40693
|
-
version2 = JSON.parse(pkgRaw).version ?? "unknown";
|
|
40694
|
-
} catch {
|
|
40695
|
-
}
|
|
41178
|
+
const { getGossipcatVersion: getGossipcatVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
41179
|
+
const version2 = getGossipcatVersion2();
|
|
40696
41180
|
let gitHead = "unknown";
|
|
40697
41181
|
try {
|
|
40698
41182
|
const result = await execFile4("git", ["rev-parse", "--short", "HEAD"]);
|
|
@@ -40750,8 +41234,10 @@ function touchSession(sid) {
|
|
|
40750
41234
|
`);
|
|
40751
41235
|
}, HTTP_SESSION_TTL_MS);
|
|
40752
41236
|
}
|
|
40753
|
-
function startHttpMcpTransport() {
|
|
40754
|
-
const
|
|
41237
|
+
async function startHttpMcpTransport() {
|
|
41238
|
+
const httpPick = await pickStickyPort("GOSSIPCAT_HTTP_PORT", HTTP_MCP_STICKY_FILE);
|
|
41239
|
+
const port = httpPick.source === "auto" ? 24421 : httpPick.port;
|
|
41240
|
+
ctx.httpMcpPortSource = httpPick.source;
|
|
40755
41241
|
const token = process.env.GOSSIPCAT_HTTP_TOKEN ?? "";
|
|
40756
41242
|
const bindHost = process.env.GOSSIPCAT_HTTP_BIND === "0.0.0.0" && token ? "0.0.0.0" : "127.0.0.1";
|
|
40757
41243
|
const tokenBuf = token ? Buffer.from(token) : null;
|
|
@@ -40774,10 +41260,10 @@ function startHttpMcpTransport() {
|
|
|
40774
41260
|
}
|
|
40775
41261
|
let body;
|
|
40776
41262
|
if (req.method === "POST") {
|
|
40777
|
-
const raw = await new Promise((
|
|
41263
|
+
const raw = await new Promise((resolve18, reject) => {
|
|
40778
41264
|
const chunks = [];
|
|
40779
41265
|
req.on("data", (c) => chunks.push(c));
|
|
40780
|
-
req.on("end", () =>
|
|
41266
|
+
req.on("end", () => resolve18(Buffer.concat(chunks).toString("utf-8")));
|
|
40781
41267
|
req.on("error", reject);
|
|
40782
41268
|
});
|
|
40783
41269
|
try {
|
|
@@ -40845,9 +41331,13 @@ function startHttpMcpTransport() {
|
|
|
40845
41331
|
res.end(JSON.stringify({ error: "Bad request" }));
|
|
40846
41332
|
});
|
|
40847
41333
|
httpServer.listen(port, bindHost, () => {
|
|
41334
|
+
const addr = httpServer.address();
|
|
41335
|
+
const bound = typeof addr === "object" && addr ? addr.port : port;
|
|
41336
|
+
ctx.httpMcpPort = bound;
|
|
41337
|
+
if (bound > 0) writeStickyPort(HTTP_MCP_STICKY_FILE, bound);
|
|
40848
41338
|
const authNote = token ? " (token protected)" : " (no auth \u2014 set GOSSIPCAT_HTTP_TOKEN to secure)";
|
|
40849
41339
|
const bindNote = bindHost === "0.0.0.0" ? " [remote]" : " [localhost only]";
|
|
40850
|
-
process.stderr.write(`[gossipcat] HTTP MCP listening on :${
|
|
41340
|
+
process.stderr.write(`[gossipcat] HTTP MCP listening on :${bound}/mcp${authNote}${bindNote}
|
|
40851
41341
|
`);
|
|
40852
41342
|
});
|
|
40853
41343
|
httpServer.on("error", (err) => {
|