svamp-cli 0.2.356 → 0.2.358
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agentCommands-D91RcjqQ.mjs → agentCommands-C_KvvYFp.mjs} +5 -5
- package/dist/{claudeAuth-C0wc1GTM.mjs → claudeAuth-DMDvPNhO.mjs} +11 -6
- package/dist/cli.mjs +136 -67
- package/dist/{commands-CoOc4qiw.mjs → commands-B19lVZjm.mjs} +10 -3
- package/dist/{commands-C13-yE2R.mjs → commands-Bmoy6Rlw.mjs} +5 -4
- package/dist/{commands-sPvRcG3J.mjs → commands-CFBKkfX8.mjs} +9 -9
- package/dist/{commands-B-yczgz_.mjs → commands-CcdoQIkb.mjs} +114 -17
- package/dist/{commands-BMeDrpSv.mjs → commands-DH_YQrAP.mjs} +5 -21
- package/dist/{commands-BbmypSgx.mjs → commands-DRxANaS5.mjs} +61 -18
- package/dist/{commands-BkdbelFo.mjs → commands-nkvTnUJj.mjs} +6 -5
- package/dist/friendlyName-B-OEwt0t.mjs +341 -0
- package/dist/{headlessCli-C8HzQvis.mjs → headlessCli-BpTXz6FD.mjs} +24 -16
- package/dist/{hookSettings-FFc1pi8j.mjs → hookSettings-CvvUhwCK.mjs} +122 -30
- package/dist/{friendlyName-1UZcyDhu.mjs → inboxGuard-CDqmIypF.mjs} +32 -347
- package/dist/index.mjs +7 -6
- package/dist/{loopVerify-DALtTk2V.mjs → loopVerify-CxAKJB5a.mjs} +1 -1
- package/dist/package-DTTWvJbp.mjs +64 -0
- package/dist/{rpc-ccY7xvlo.mjs → rpc-CAlAHQAy.mjs} +3 -1
- package/dist/{rpc-CBkSkzsI.mjs → rpc-CacreLQ8.mjs} +10 -6
- package/dist/{run-DCq3aEiF.mjs → run-Bc81iurl.mjs} +26 -7
- package/dist/{run-DDmeV9K3.mjs → run-DKOND4dO.mjs} +48 -30
- package/dist/{scheduler-BkVJ27Ki.mjs → scheduler-IyLWBF8T.mjs} +25 -7
- package/dist/{serveCommands-CLhkccJM.mjs → serveCommands-B-Ym_QXc.mjs} +8 -8
- package/dist/{store-B4E6NzG6.mjs → store-D8dok6_d.mjs} +53 -14
- package/package.json +3 -3
- package/dist/package-BrsiNutS.mjs +0 -64
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
const ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
4
|
+
const LOWER_ALPHABET = "abcdefghijklmnopqrstuvwxyz";
|
|
5
|
+
function shortId(length = 10, alphabet = ALPHABET) {
|
|
6
|
+
const maxUnbiased = Math.floor(256 / alphabet.length) * alphabet.length;
|
|
7
|
+
let out = "";
|
|
8
|
+
while (out.length < length) {
|
|
9
|
+
const buf = randomBytes(length - out.length);
|
|
10
|
+
for (let i = 0; i < buf.length && out.length < length; i++) {
|
|
11
|
+
const b = buf[i];
|
|
12
|
+
if (b < maxUnbiased) out += alphabet[b % alphabet.length];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ADJECTIVES = [
|
|
19
|
+
"able",
|
|
20
|
+
"amber",
|
|
21
|
+
"amused",
|
|
22
|
+
"ancient",
|
|
23
|
+
"arctic",
|
|
24
|
+
"autumn",
|
|
25
|
+
"azure",
|
|
26
|
+
"blithe",
|
|
27
|
+
"bold",
|
|
28
|
+
"brave",
|
|
29
|
+
"breezy",
|
|
30
|
+
"bright",
|
|
31
|
+
"brisk",
|
|
32
|
+
"calm",
|
|
33
|
+
"candid",
|
|
34
|
+
"cheery",
|
|
35
|
+
"chill",
|
|
36
|
+
"clever",
|
|
37
|
+
"cobalt",
|
|
38
|
+
"cosmic",
|
|
39
|
+
"cozy",
|
|
40
|
+
"crimson",
|
|
41
|
+
"crisp",
|
|
42
|
+
"curious",
|
|
43
|
+
"dapper",
|
|
44
|
+
"daring",
|
|
45
|
+
"dawn",
|
|
46
|
+
"deft",
|
|
47
|
+
"dewy",
|
|
48
|
+
"eager",
|
|
49
|
+
"early",
|
|
50
|
+
"easy",
|
|
51
|
+
"electric",
|
|
52
|
+
"fancy",
|
|
53
|
+
"feisty",
|
|
54
|
+
"fleet",
|
|
55
|
+
"fond",
|
|
56
|
+
"frosty",
|
|
57
|
+
"gallant",
|
|
58
|
+
"gentle",
|
|
59
|
+
"giddy",
|
|
60
|
+
"glad",
|
|
61
|
+
"gleaming",
|
|
62
|
+
"golden",
|
|
63
|
+
"graceful",
|
|
64
|
+
"grand",
|
|
65
|
+
"hardy",
|
|
66
|
+
"hazel",
|
|
67
|
+
"hearty",
|
|
68
|
+
"honest",
|
|
69
|
+
"humble",
|
|
70
|
+
"jolly",
|
|
71
|
+
"jovial",
|
|
72
|
+
"keen",
|
|
73
|
+
"kind",
|
|
74
|
+
"lively",
|
|
75
|
+
"loyal",
|
|
76
|
+
"lucky",
|
|
77
|
+
"lunar",
|
|
78
|
+
"mellow",
|
|
79
|
+
"merry",
|
|
80
|
+
"mighty",
|
|
81
|
+
"mint",
|
|
82
|
+
"misty",
|
|
83
|
+
"nimble",
|
|
84
|
+
"noble",
|
|
85
|
+
"opal",
|
|
86
|
+
"patient",
|
|
87
|
+
"peppy",
|
|
88
|
+
"placid",
|
|
89
|
+
"plucky",
|
|
90
|
+
"polar",
|
|
91
|
+
"prim",
|
|
92
|
+
"proud",
|
|
93
|
+
"quick",
|
|
94
|
+
"quiet",
|
|
95
|
+
"quirky",
|
|
96
|
+
"radiant",
|
|
97
|
+
"rapid",
|
|
98
|
+
"ready",
|
|
99
|
+
"regal",
|
|
100
|
+
"rosy",
|
|
101
|
+
"royal",
|
|
102
|
+
"rugged",
|
|
103
|
+
"sage",
|
|
104
|
+
"sandy",
|
|
105
|
+
"scarlet",
|
|
106
|
+
"serene",
|
|
107
|
+
"sharp",
|
|
108
|
+
"shiny",
|
|
109
|
+
"silent",
|
|
110
|
+
"silver",
|
|
111
|
+
"sleek",
|
|
112
|
+
"smooth",
|
|
113
|
+
"snappy",
|
|
114
|
+
"snowy",
|
|
115
|
+
"solar",
|
|
116
|
+
"spry",
|
|
117
|
+
"stellar",
|
|
118
|
+
"sturdy",
|
|
119
|
+
"sunny",
|
|
120
|
+
"swift",
|
|
121
|
+
"tender",
|
|
122
|
+
"tidal",
|
|
123
|
+
"tidy",
|
|
124
|
+
"tranquil",
|
|
125
|
+
"trusty",
|
|
126
|
+
"upbeat",
|
|
127
|
+
"valiant",
|
|
128
|
+
"vivid",
|
|
129
|
+
"warm",
|
|
130
|
+
"whimsical",
|
|
131
|
+
"wise",
|
|
132
|
+
"witty",
|
|
133
|
+
"zesty",
|
|
134
|
+
"zippy"
|
|
135
|
+
];
|
|
136
|
+
const ANIMALS = [
|
|
137
|
+
"ant",
|
|
138
|
+
"badger",
|
|
139
|
+
"bat",
|
|
140
|
+
"bear",
|
|
141
|
+
"beaver",
|
|
142
|
+
"bee",
|
|
143
|
+
"bison",
|
|
144
|
+
"boar",
|
|
145
|
+
"bobcat",
|
|
146
|
+
"buffalo",
|
|
147
|
+
"camel",
|
|
148
|
+
"caribou",
|
|
149
|
+
"cat",
|
|
150
|
+
"cheetah",
|
|
151
|
+
"cobra",
|
|
152
|
+
"condor",
|
|
153
|
+
"cougar",
|
|
154
|
+
"coyote",
|
|
155
|
+
"crab",
|
|
156
|
+
"crane",
|
|
157
|
+
"cricket",
|
|
158
|
+
"crow",
|
|
159
|
+
"deer",
|
|
160
|
+
"dingo",
|
|
161
|
+
"dolphin",
|
|
162
|
+
"donkey",
|
|
163
|
+
"dove",
|
|
164
|
+
"dragon",
|
|
165
|
+
"duck",
|
|
166
|
+
"eagle",
|
|
167
|
+
"eel",
|
|
168
|
+
"egret",
|
|
169
|
+
"elk",
|
|
170
|
+
"falcon",
|
|
171
|
+
"ferret",
|
|
172
|
+
"finch",
|
|
173
|
+
"fox",
|
|
174
|
+
"frog",
|
|
175
|
+
"gecko",
|
|
176
|
+
"gibbon",
|
|
177
|
+
"goat",
|
|
178
|
+
"goose",
|
|
179
|
+
"gopher",
|
|
180
|
+
"hare",
|
|
181
|
+
"hawk",
|
|
182
|
+
"hedgehog",
|
|
183
|
+
"heron",
|
|
184
|
+
"hippo",
|
|
185
|
+
"horse",
|
|
186
|
+
"ibex",
|
|
187
|
+
"ibis",
|
|
188
|
+
"iguana",
|
|
189
|
+
"jackal",
|
|
190
|
+
"jaguar",
|
|
191
|
+
"jay",
|
|
192
|
+
"kestrel",
|
|
193
|
+
"koala",
|
|
194
|
+
"krill",
|
|
195
|
+
"lark",
|
|
196
|
+
"lemur",
|
|
197
|
+
"leopard",
|
|
198
|
+
"lion",
|
|
199
|
+
"llama",
|
|
200
|
+
"lynx",
|
|
201
|
+
"macaw",
|
|
202
|
+
"magpie",
|
|
203
|
+
"mantis",
|
|
204
|
+
"marmot",
|
|
205
|
+
"marten",
|
|
206
|
+
"meerkat",
|
|
207
|
+
"mink",
|
|
208
|
+
"mole",
|
|
209
|
+
"moose",
|
|
210
|
+
"moth",
|
|
211
|
+
"mouse",
|
|
212
|
+
"newt",
|
|
213
|
+
"ocelot",
|
|
214
|
+
"octopus",
|
|
215
|
+
"orca",
|
|
216
|
+
"osprey",
|
|
217
|
+
"otter",
|
|
218
|
+
"owl",
|
|
219
|
+
"ox",
|
|
220
|
+
"panda",
|
|
221
|
+
"panther",
|
|
222
|
+
"parrot",
|
|
223
|
+
"pelican",
|
|
224
|
+
"penguin",
|
|
225
|
+
"pheasant",
|
|
226
|
+
"pigeon",
|
|
227
|
+
"puffin",
|
|
228
|
+
"puma",
|
|
229
|
+
"quail",
|
|
230
|
+
"rabbit",
|
|
231
|
+
"raccoon",
|
|
232
|
+
"ram",
|
|
233
|
+
"raven",
|
|
234
|
+
"robin",
|
|
235
|
+
"salmon",
|
|
236
|
+
"seal",
|
|
237
|
+
"shark",
|
|
238
|
+
"sheep",
|
|
239
|
+
"shrew",
|
|
240
|
+
"skunk",
|
|
241
|
+
"sloth",
|
|
242
|
+
"snail",
|
|
243
|
+
"sparrow",
|
|
244
|
+
"spider",
|
|
245
|
+
"squid",
|
|
246
|
+
"stag",
|
|
247
|
+
"stoat",
|
|
248
|
+
"stork",
|
|
249
|
+
"swan",
|
|
250
|
+
"tapir",
|
|
251
|
+
"tiger",
|
|
252
|
+
"toad",
|
|
253
|
+
"trout",
|
|
254
|
+
"turtle",
|
|
255
|
+
"viper",
|
|
256
|
+
"vole",
|
|
257
|
+
"walrus",
|
|
258
|
+
"weasel",
|
|
259
|
+
"whale",
|
|
260
|
+
"wolf",
|
|
261
|
+
"wombat",
|
|
262
|
+
"wren",
|
|
263
|
+
"yak",
|
|
264
|
+
"zebra"
|
|
265
|
+
];
|
|
266
|
+
function pick(arr) {
|
|
267
|
+
const n = arr.length;
|
|
268
|
+
const max = Math.floor(256 / n) * n;
|
|
269
|
+
let b;
|
|
270
|
+
do {
|
|
271
|
+
b = randomBytes(1)[0];
|
|
272
|
+
} while (b >= max);
|
|
273
|
+
return arr[b % n];
|
|
274
|
+
}
|
|
275
|
+
function generateFriendlyName(taken = []) {
|
|
276
|
+
const used = taken instanceof Set ? taken : new Set(taken);
|
|
277
|
+
for (let i = 0; i < 1e3; i++) {
|
|
278
|
+
const name = `${pick(ADJECTIVES)}-${pick(ANIMALS)}`;
|
|
279
|
+
if (!used.has(name)) return name;
|
|
280
|
+
}
|
|
281
|
+
const base = `${pick(ADJECTIVES)}-${pick(ANIMALS)}`;
|
|
282
|
+
let n = 2;
|
|
283
|
+
while (used.has(`${base}-${n}`)) n++;
|
|
284
|
+
return `${base}-${n}`;
|
|
285
|
+
}
|
|
286
|
+
function sanitizeSegment(s) {
|
|
287
|
+
return String(s || "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
288
|
+
}
|
|
289
|
+
function friendlyNameFromId(id) {
|
|
290
|
+
const m = /^(.+)-[a-zA-Z0-9]{6,10}$/.exec(String(id || ""));
|
|
291
|
+
return m ? m[1] : void 0;
|
|
292
|
+
}
|
|
293
|
+
function composeSessionId(friendlyName, takenIds = [], len = 6) {
|
|
294
|
+
const taken = takenIds instanceof Set ? takenIds : new Set(takenIds);
|
|
295
|
+
const A = LOWER_ALPHABET.length;
|
|
296
|
+
const nthSuffix = (i, width2) => {
|
|
297
|
+
let s = "";
|
|
298
|
+
for (let k = 0; k < width2; k++) {
|
|
299
|
+
s = LOWER_ALPHABET[i % A] + s;
|
|
300
|
+
i = Math.floor(i / A);
|
|
301
|
+
}
|
|
302
|
+
return s;
|
|
303
|
+
};
|
|
304
|
+
let width = Math.max(1, len);
|
|
305
|
+
for (; ; ) {
|
|
306
|
+
const space = Math.pow(A, width);
|
|
307
|
+
for (let i = 0; i < 64; i++) {
|
|
308
|
+
const candidate = `${friendlyName}-${shortId(width, LOWER_ALPHABET)}`;
|
|
309
|
+
if (!taken.has(candidate)) return candidate;
|
|
310
|
+
}
|
|
311
|
+
if (space <= 1e6) {
|
|
312
|
+
for (let i = 0; i < space; i++) {
|
|
313
|
+
const candidate = `${friendlyName}-${nthSuffix(i, width)}`;
|
|
314
|
+
if (!taken.has(candidate)) return candidate;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
width++;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function formatHandle(projectName, friendlyName) {
|
|
321
|
+
if (!friendlyName) return void 0;
|
|
322
|
+
const proj = sanitizeSegment(projectName || "");
|
|
323
|
+
return proj ? `${proj}:${friendlyName}` : friendlyName;
|
|
324
|
+
}
|
|
325
|
+
function parseHandle(input) {
|
|
326
|
+
const str = String(input || "").trim().replace(/^@/, "");
|
|
327
|
+
if (!str) return null;
|
|
328
|
+
const i = str.indexOf(":");
|
|
329
|
+
if (i === -1) return { name: str.toLowerCase() };
|
|
330
|
+
return { project: sanitizeSegment(str.slice(0, i)), name: str.slice(i + 1).toLowerCase() };
|
|
331
|
+
}
|
|
332
|
+
function handleMatchesMetadata(parsed, metadata) {
|
|
333
|
+
const fn = String(metadata?.friendlyName || "").toLowerCase();
|
|
334
|
+
if (!fn || fn !== parsed.name) return false;
|
|
335
|
+
if (parsed.project) {
|
|
336
|
+
return sanitizeSegment(metadata?.projectName || "") === parsed.project;
|
|
337
|
+
}
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export { friendlyNameFromId as a, composeSessionId as c, formatHandle as f, generateFriendlyName as g, handleMatchesMetadata as h, parseHandle as p, shortId as s };
|
|
@@ -261,6 +261,10 @@ async function createPorcupineWakeWord(opts) {
|
|
|
261
261
|
};
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
const DEFAULT_REALTIME_MODEL = "gpt-realtime-mini";
|
|
265
|
+
function resolveRealtimeModel(explicit, env) {
|
|
266
|
+
return explicit || env.WISE_VOICE_MODEL || DEFAULT_REALTIME_MODEL;
|
|
267
|
+
}
|
|
264
268
|
async function runWiseVoiceCli(opts = {}) {
|
|
265
269
|
const resolved = resolveModel({ provider: "openai" }, process.env);
|
|
266
270
|
const mis = describeMisconfiguration(resolved);
|
|
@@ -286,30 +290,34 @@ async function runWiseVoiceCli(opts = {}) {
|
|
|
286
290
|
}
|
|
287
291
|
}
|
|
288
292
|
const audio = createSoxAudioAdapter();
|
|
289
|
-
|
|
293
|
+
const realtimeModel = resolveRealtimeModel(opts.model, process.env);
|
|
294
|
+
console.log(`\u25CF WISE headless voice \u2014 ${tools.length} tools | model ${realtimeModel} | ${wakeWord ? `wake: "${opts.wakeKeywordPath}"` : "always-listening (Ctrl-C to stop)"}`);
|
|
290
295
|
const loop = runHeadlessVoice({
|
|
291
296
|
apiKey: resolved.apiKey,
|
|
292
297
|
baseUrl: resolved.baseUrl,
|
|
293
|
-
config: { instructions, tools, voice: opts.voice, model:
|
|
298
|
+
config: { instructions, tools, voice: opts.voice, model: realtimeModel },
|
|
299
|
+
// #1710
|
|
294
300
|
audio,
|
|
295
301
|
wakeWord,
|
|
296
302
|
onTool: (t) => console.log(` \u21B3 ${t.name}: ${t.output.slice(0, 120).replace(/\n/g, " ")}`),
|
|
297
303
|
onError: (e) => console.error(` ! ${e.message}`)
|
|
298
304
|
});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
305
|
+
await new Promise((resolve) => {
|
|
306
|
+
const shutdown = () => {
|
|
307
|
+
console.log("\nstopping\u2026");
|
|
308
|
+
try {
|
|
309
|
+
loop.stop();
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
312
|
+
try {
|
|
313
|
+
audio.close();
|
|
314
|
+
} catch {
|
|
315
|
+
}
|
|
316
|
+
resolve();
|
|
317
|
+
};
|
|
318
|
+
process.on("SIGINT", shutdown);
|
|
319
|
+
process.on("SIGTERM", shutdown);
|
|
320
|
+
});
|
|
313
321
|
}
|
|
314
322
|
|
|
315
323
|
export { runWiseVoiceCli };
|
|
@@ -22,10 +22,10 @@ import { existsSync as existsSync$1, mkdirSync as mkdirSync$1, readFileSync as r
|
|
|
22
22
|
import os, { homedir as homedir$1 } from 'node:os';
|
|
23
23
|
import { join as join$1, extname, resolve, sep, basename, dirname } from 'node:path';
|
|
24
24
|
import { e as envFlagEnabled, a as envFlagDisabled } from './envFlag-COOuJ3AH.mjs';
|
|
25
|
-
import { normalizeProxyUrl, resolveHyphaProxyUrl, applyClaudeProxyEnv } from './claudeAuth-
|
|
25
|
+
import { normalizeProxyUrl, resolveHyphaProxyUrl, applyClaudeProxyEnv } from './claudeAuth-DMDvPNhO.mjs';
|
|
26
26
|
import { CODEX_PROVIDER_KEY_ENV } from './codexProvider-BtxYc5Rj.mjs';
|
|
27
27
|
import { spawn as spawn$1 } from 'node:child_process';
|
|
28
|
-
import {
|
|
28
|
+
import { applyInboxClear } from './inboxGuard-CDqmIypF.mjs';
|
|
29
29
|
import { w as withFileLock, s as sleepSync } from './fileLock-BMPwCrB6.mjs';
|
|
30
30
|
import { EventEmitter } from 'node:events';
|
|
31
31
|
import { r as renderTemplate } from './cron-hHGdb5kU.mjs';
|
|
@@ -603,8 +603,11 @@ class ProcessSupervisor {
|
|
|
603
603
|
if (!file.endsWith(".json")) continue;
|
|
604
604
|
try {
|
|
605
605
|
const raw = await readFile(path__default.join(this.persistDir, file), "utf-8");
|
|
606
|
-
const
|
|
607
|
-
const
|
|
606
|
+
const parsed = JSON.parse(raw);
|
|
607
|
+
const restored = parsed?.__state;
|
|
608
|
+
delete parsed.__state;
|
|
609
|
+
const spec = parsed;
|
|
610
|
+
const entry = this.makeEntry(spec, restored);
|
|
608
611
|
this.entries.set(spec.id, entry);
|
|
609
612
|
if (spec.keepAlive && !spec.desiredStopped) {
|
|
610
613
|
await this.startEntry(
|
|
@@ -627,12 +630,49 @@ class ProcessSupervisor {
|
|
|
627
630
|
console.log(`[SUPERVISOR] Restored ${loaded} supervised process(es)`);
|
|
628
631
|
}
|
|
629
632
|
}
|
|
630
|
-
|
|
633
|
+
/**
|
|
634
|
+
* #1643: the crash-loop LEDGER travels with the spec.
|
|
635
|
+
*
|
|
636
|
+
* `ProcessState` is annotated "not persisted", and `makeEntry` zeroes restartCount /
|
|
637
|
+
* consecutiveFailures / crashLoopBackOff on every restore — so a permanently-broken keepAlive
|
|
638
|
+
* spec (dead upstream, taken port, vanished workdir) got a FRESH 10-restart budget on every
|
|
639
|
+
* daemon start, forever. It burns CRASH_LOOP_FAILURE_THRESHOLD restarts and ~15 min of
|
|
640
|
+
* exponential spawn churn, parks crash-loop-backoff, and starts over on the next boot —
|
|
641
|
+
* undermining the cap that exists because of the "11,508 restarts in 4 days" incident, and
|
|
642
|
+
* worst right after a reboot when several dead specs churn at once. `svamp process list` also
|
|
643
|
+
* showed RESTARTS 0 after every restart, which hid it. The #1633 docblock states this for the
|
|
644
|
+
* missing-workdir case; it is general.
|
|
645
|
+
*
|
|
646
|
+
* Written under a `__state` key so the file stays a superset of ProcessSpec: an older daemon
|
|
647
|
+
* ignores it, and `loadAll` strips it before the object becomes `entry.spec`.
|
|
648
|
+
*/
|
|
649
|
+
async persistSpec(spec, state, failureWindowStart) {
|
|
631
650
|
const filePath = path__default.join(this.persistDir, `${spec.id}.json`);
|
|
651
|
+
if (!state) {
|
|
652
|
+
const live = this.entries.get(spec.id);
|
|
653
|
+
if (live) {
|
|
654
|
+
state = live.state;
|
|
655
|
+
failureWindowStart = live.failureWindowStart;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
632
658
|
const tmpPath = `${filePath}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
633
|
-
|
|
659
|
+
const payload = { ...spec };
|
|
660
|
+
if (state) {
|
|
661
|
+
payload.__state = {
|
|
662
|
+
restartCount: state.restartCount,
|
|
663
|
+
userRestartCount: state.userRestartCount,
|
|
664
|
+
consecutiveFailures: state.consecutiveFailures,
|
|
665
|
+
crashLoopBackOff: state.crashLoopBackOff,
|
|
666
|
+
failureWindowStart
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
await writeFile(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
634
670
|
await rename(tmpPath, filePath);
|
|
635
671
|
}
|
|
672
|
+
/** #1643: rewrite this spec's file including its live crash-loop ledger. Best-effort. */
|
|
673
|
+
persistLedger(entry) {
|
|
674
|
+
void this.persistSpec(entry.spec, entry.state, entry.failureWindowStart).catch((err) => console.error(`[SUPERVISOR] Failed to persist crash-loop ledger for ${entry.spec.name}: ${err?.message || err}`));
|
|
675
|
+
}
|
|
636
676
|
async deleteSpec(id) {
|
|
637
677
|
try {
|
|
638
678
|
await unlink(path__default.join(this.persistDir, `${id}.json`));
|
|
@@ -640,17 +680,22 @@ class ProcessSupervisor {
|
|
|
640
680
|
}
|
|
641
681
|
}
|
|
642
682
|
// ── Internal helpers ──────────────────────────────────────────────────────
|
|
643
|
-
makeEntry(spec) {
|
|
683
|
+
makeEntry(spec, restored) {
|
|
644
684
|
return {
|
|
645
685
|
spec,
|
|
646
686
|
state: {
|
|
647
687
|
id: spec.id,
|
|
648
688
|
status: "pending",
|
|
649
|
-
|
|
650
|
-
|
|
689
|
+
// #1643: a restored ledger carries the crash-loop budget ACROSS the restart. Without
|
|
690
|
+
// it, `maxRestarts` and CRASH_LOOP_FAILURE_THRESHOLD were per-boot rather than
|
|
691
|
+
// lifetime caps, which is not what either of them claims to be.
|
|
692
|
+
restartCount: restored?.restartCount ?? 0,
|
|
693
|
+
userRestartCount: restored?.userRestartCount ?? 0,
|
|
651
694
|
consecutiveProbeFailures: 0,
|
|
652
|
-
consecutiveFailures: 0
|
|
695
|
+
consecutiveFailures: restored?.consecutiveFailures ?? 0,
|
|
696
|
+
crashLoopBackOff: restored?.crashLoopBackOff
|
|
653
697
|
},
|
|
698
|
+
failureWindowStart: restored?.failureWindowStart,
|
|
654
699
|
logBuffer: [],
|
|
655
700
|
stopping: false
|
|
656
701
|
};
|
|
@@ -781,6 +826,7 @@ class ProcessSupervisor {
|
|
|
781
826
|
);
|
|
782
827
|
return;
|
|
783
828
|
}
|
|
829
|
+
this.persistLedger(entry);
|
|
784
830
|
if (spec.maxRestarts > 0 && state.restartCount >= spec.maxRestarts) {
|
|
785
831
|
console.warn(`[SUPERVISOR] Process '${spec.name}' reached max restarts (${spec.maxRestarts}), not restarting`);
|
|
786
832
|
state.status = "failed";
|
|
@@ -804,6 +850,7 @@ class ProcessSupervisor {
|
|
|
804
850
|
if (entry.child) return;
|
|
805
851
|
state.restartCount++;
|
|
806
852
|
state.status = "starting";
|
|
853
|
+
this.persistLedger(entry);
|
|
807
854
|
this.spawnProcess(entry);
|
|
808
855
|
}, delayMs);
|
|
809
856
|
}
|
|
@@ -6066,6 +6113,15 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
6066
6113
|
const callerEmail = context?.user?.email;
|
|
6067
6114
|
const machineOwner = currentMetadata.sharing?.owner;
|
|
6068
6115
|
const isSharedUser = callerEmail && machineOwner && callerEmail.toLowerCase() !== machineOwner.toLowerCase();
|
|
6116
|
+
if (options.sharing?.allowedUsers?.length) {
|
|
6117
|
+
options = {
|
|
6118
|
+
...options,
|
|
6119
|
+
sharing: {
|
|
6120
|
+
...options.sharing,
|
|
6121
|
+
allowedUsers: options.sharing.allowedUsers.map((u) => normalizeAllowedUser(u, "spawn"))
|
|
6122
|
+
}
|
|
6123
|
+
};
|
|
6124
|
+
}
|
|
6069
6125
|
if (isSharedUser) {
|
|
6070
6126
|
const sharing = {
|
|
6071
6127
|
enabled: true,
|
|
@@ -7570,7 +7626,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
7570
7626
|
return { ok: false, call_id: callId, status: "busy", error: "channel is busy (too many concurrent requests) \u2014 retry shortly" };
|
|
7571
7627
|
}
|
|
7572
7628
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
7573
|
-
const { queryCore } = await import('./commands-
|
|
7629
|
+
const { queryCore } = await import('./commands-CcdoQIkb.mjs').then(function (n) { return n.d; });
|
|
7574
7630
|
const timeout = c.reply?.timeout_sec || 120;
|
|
7575
7631
|
let result;
|
|
7576
7632
|
let thrownSessionId;
|
|
@@ -7957,6 +8013,14 @@ function loadInbox(projectDir, sessionId) {
|
|
|
7957
8013
|
return [];
|
|
7958
8014
|
}
|
|
7959
8015
|
}
|
|
8016
|
+
const reportedInboxWriteErrnos = /* @__PURE__ */ new Set();
|
|
8017
|
+
function reportInboxWriteFailure(projectDir, sessionId, err) {
|
|
8018
|
+
const code = String(err?.code || err?.name || "UNKNOWN");
|
|
8019
|
+
if (reportedInboxWriteErrnos.has(code)) return;
|
|
8020
|
+
reportedInboxWriteErrnos.add(code);
|
|
8021
|
+
const hint = code === "ENOSPC" ? ' \u2014 THE DISK IS FULL. Pending inter-agent messages will be LOST on the next daemon restart and `inbox reply` will answer "not found"; session transcripts are likely failing too.' : code === "EROFS" ? " \u2014 the filesystem is read-only." : code === "EACCES" || code === "EPERM" ? " \u2014 permission denied on the session .svamp directory." : code === "ENOTDIR" ? " \u2014 a path component of .svamp/<sessionId>/ is a FILE, not a directory." : "";
|
|
8022
|
+
console.error(`[inbox] persist failed for session ${sessionId} in ${projectDir} (${code})${hint} Further ${code} failures will not be repeated. (#1714)`);
|
|
8023
|
+
}
|
|
7960
8024
|
function saveInbox(projectDir, sessionId, inbox) {
|
|
7961
8025
|
try {
|
|
7962
8026
|
const p = inboxFilePath(projectDir, sessionId);
|
|
@@ -7964,7 +8028,8 @@ function saveInbox(projectDir, sessionId, inbox) {
|
|
|
7964
8028
|
const tmp = `${p}.tmp-${process.pid}-${randomBytes$1(4).toString("hex")}`;
|
|
7965
8029
|
writeFileSync$1(tmp, JSON.stringify(inbox));
|
|
7966
8030
|
renameSync(tmp, p);
|
|
7967
|
-
} catch {
|
|
8031
|
+
} catch (err) {
|
|
8032
|
+
reportInboxWriteFailure(projectDir, sessionId, err);
|
|
7968
8033
|
}
|
|
7969
8034
|
}
|
|
7970
8035
|
function isPending(m) {
|
|
@@ -8255,25 +8320,34 @@ async function _mintPairingCodeLocked(opts, resolver, ttlMs) {
|
|
|
8255
8320
|
stage: true,
|
|
8256
8321
|
_rkwargs: true
|
|
8257
8322
|
});
|
|
8258
|
-
const putUrl = await am.put_file({ artifact_id: artifactId, file_path: "pair.json", download_weight: 0, _rkwargs: true });
|
|
8259
|
-
if (!putUrl || typeof putUrl !== "string") throw new Error(`put_file returned invalid URL: ${putUrl}`);
|
|
8260
|
-
const controller = new AbortController();
|
|
8261
|
-
const timer = setTimeout(() => controller.abort(), 3e4);
|
|
8262
|
-
try {
|
|
8263
|
-
const resp = await fetch(putUrl, {
|
|
8264
|
-
method: "PUT",
|
|
8265
|
-
body: pairJson,
|
|
8266
|
-
headers: { "Content-Type": "application/json" },
|
|
8267
|
-
signal: controller.signal
|
|
8268
|
-
});
|
|
8269
|
-
if (!resp.ok) throw new Error(`pair.json upload failed: ${resp.status} ${resp.statusText}`);
|
|
8270
|
-
} finally {
|
|
8271
|
-
clearTimeout(timer);
|
|
8272
|
-
}
|
|
8273
|
-
await am.commit({ artifact_id: artifactId, _rkwargs: true });
|
|
8274
8323
|
const all = loadPairings(opts.svampHome);
|
|
8275
8324
|
all.push({ session, code, artifactId, expiresAt, createdAt: now });
|
|
8276
8325
|
savePairings(all, opts.svampHome);
|
|
8326
|
+
try {
|
|
8327
|
+
const putUrl = await am.put_file({ artifact_id: artifactId, file_path: "pair.json", download_weight: 0, _rkwargs: true });
|
|
8328
|
+
if (!putUrl || typeof putUrl !== "string") throw new Error(`put_file returned invalid URL: ${putUrl}`);
|
|
8329
|
+
const controller = new AbortController();
|
|
8330
|
+
const timer = setTimeout(() => controller.abort(), 3e4);
|
|
8331
|
+
try {
|
|
8332
|
+
const resp = await fetch(putUrl, {
|
|
8333
|
+
method: "PUT",
|
|
8334
|
+
body: pairJson,
|
|
8335
|
+
headers: { "Content-Type": "application/json" },
|
|
8336
|
+
signal: controller.signal
|
|
8337
|
+
});
|
|
8338
|
+
if (!resp.ok) throw new Error(`pair.json upload failed: ${resp.status} ${resp.statusText}`);
|
|
8339
|
+
} finally {
|
|
8340
|
+
clearTimeout(timer);
|
|
8341
|
+
}
|
|
8342
|
+
await am.commit({ artifact_id: artifactId, _rkwargs: true });
|
|
8343
|
+
} catch (err) {
|
|
8344
|
+
try {
|
|
8345
|
+
await deletePairingArtifact(am, artifactId, log);
|
|
8346
|
+
savePairings(loadPairings(opts.svampHome).filter((p) => p.artifactId !== artifactId), opts.svampHome);
|
|
8347
|
+
} catch {
|
|
8348
|
+
}
|
|
8349
|
+
throw err;
|
|
8350
|
+
}
|
|
8277
8351
|
log?.(`[OUTPOST PAIRING] minted ${code} for ${session} (expires in ${Math.round(ttlMs / 6e4)}m)`);
|
|
8278
8352
|
return { code, resolver, expiresAt };
|
|
8279
8353
|
}
|
|
@@ -8499,6 +8573,24 @@ function channelPublicView(c) {
|
|
|
8499
8573
|
function isStructuredMessage(msg) {
|
|
8500
8574
|
return !!(msg.from || msg.fromSession || msg.subject || msg.replyTo || msg.threadId || msg.channel);
|
|
8501
8575
|
}
|
|
8576
|
+
function reportInboxEviction(result, sessionId) {
|
|
8577
|
+
for (const m of result.droppedUndelivered) {
|
|
8578
|
+
console.warn(
|
|
8579
|
+
`[HYPHA SESSION ${sessionId}] inbox at cap: DROPPED AN UNDELIVERED message ${m.messageId || "<no id>"} from ${m.from || "<unknown>"} (urgency=${m.urgency || "normal"}). Every resident message was undelivered, so there was no delivered one to evict instead. The sender was told it was sent; it is now gone (#0328).`
|
|
8580
|
+
);
|
|
8581
|
+
}
|
|
8582
|
+
}
|
|
8583
|
+
function evictInboxOverflow(inbox, max) {
|
|
8584
|
+
const dropped = [];
|
|
8585
|
+
const droppedUndelivered = [];
|
|
8586
|
+
while (inbox.length > max) {
|
|
8587
|
+
const idx = inbox.findIndex((m) => m.handled);
|
|
8588
|
+
const victim = inbox.splice(idx < 0 ? 0 : idx, 1)[0];
|
|
8589
|
+
dropped.push(victim);
|
|
8590
|
+
if (!victim.handled) droppedUndelivered.push(victim);
|
|
8591
|
+
}
|
|
8592
|
+
return { dropped, droppedUndelivered };
|
|
8593
|
+
}
|
|
8502
8594
|
function escapeXml(s) {
|
|
8503
8595
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
8504
8596
|
}
|
|
@@ -8958,7 +9050,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
8958
9050
|
subject: `Remote outpost connected: ${who}`
|
|
8959
9051
|
};
|
|
8960
9052
|
inbox.push(msg);
|
|
8961
|
-
|
|
9053
|
+
reportInboxEviction(evictInboxOverflow(inbox, INBOX_MAX), sessionId);
|
|
8962
9054
|
syncInboxToMetadata();
|
|
8963
9055
|
callbacks.onInboxMessage?.(msg);
|
|
8964
9056
|
notifyListeners({ type: "inbox-update", sessionId, message: msg });
|
|
@@ -10075,7 +10167,7 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
10075
10167
|
const trusted = !context?.user || isSameOwnerWorkspace(context) || !!metadata.sharing && getEffectiveRole(context, metadata.sharing) === "admin";
|
|
10076
10168
|
const msg = sanitizeInboundInboxMessage(message, { trusted, callerEmail: context?.user?.email });
|
|
10077
10169
|
inbox.push(msg);
|
|
10078
|
-
|
|
10170
|
+
reportInboxEviction(evictInboxOverflow(inbox, INBOX_MAX), sessionId);
|
|
10079
10171
|
syncInboxToMetadata();
|
|
10080
10172
|
callbacks.onInboxMessage?.(msg);
|
|
10081
10173
|
notifyListeners({ type: "inbox-update", sessionId, message: msg });
|