@moxxy/cli 0.14.2 → 0.14.4
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/bin.js +371 -56
- package/dist/bin.js.map +1 -1
- package/dist/sidecar.js +11 -2
- package/dist/sidecar.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { z as z$1, createMutex, defineTunnelProvider, definePlugin, defineProvider, defineTool, MoxxyError, asTurnId, defineMode, asPluginId, defineCommand, defineChannel, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, zodToJsonSchema, fileDiffSummary, runSingleShotTurn, defineSurface, runManualCompaction, isFileDiffDisplay, renderFrontmatter, defineEmbedder, migrateModeName, skillFrontmatterSchema, asSkillId, startChannelWith, parseFrontmatterFile, createDeferredPermissionResolver, getInstallHint, defineTranscriber, summarizeTokensByModel, countNodes, moxxyPackageSchema, encodeLoginPrompt, classifyNetworkError, addModelTotals, createJsonFileStore, ISOLATION_RANK, MOXXY_PCM16_24KHZ_MIME, fileDiffVerb, parseFrontmatter, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, isSafeViewUrl, evaluateToolRule, summarizeSessionTokensFromEvents, toDiffRows, diffGutterNo, computeElisionState, toolResultStubbed, toolResultStub, toolResultBytes, conversationalStubbed, conversationalStub, asEventId } from '@moxxy/sdk';
|
|
4
4
|
import * as fs32 from 'fs';
|
|
5
|
-
import fs32__default, { existsSync, promises, ReadStream, mkdirSync, statSync, readdirSync, writeFileSync, readFileSync, unlinkSync, chmodSync, watch, createReadStream } from 'fs';
|
|
5
|
+
import fs32__default, { existsSync, promises, ReadStream, mkdirSync, rmSync, statSync, readdirSync, writeFileSync, readFileSync, unlinkSync, chmodSync, watch, createReadStream } from 'fs';
|
|
6
6
|
import * as path3 from 'path';
|
|
7
7
|
import path3__default, { join, dirname, resolve, relative, isAbsolute, basename } from 'path';
|
|
8
8
|
import { isCliTunnelAvailable, writeFileAtomic, spawnCliTunnel, moxxyPath, moxxyHome, bearerTokenMatches, resolveChannelToken, rotateChannelToken, readRequestBody, MOXXY_WS_SUBPROTOCOL, bearerGuard, tokenFromWsProtocolHeader, writeFileAtomicSync } from '@moxxy/sdk/server';
|
|
@@ -3666,6 +3666,107 @@ var init_session = __esm({
|
|
|
3666
3666
|
};
|
|
3667
3667
|
}
|
|
3668
3668
|
});
|
|
3669
|
+
|
|
3670
|
+
// ../core/dist/setup-agent.js
|
|
3671
|
+
function resolvePermissions(p3) {
|
|
3672
|
+
if (p3 === "deny")
|
|
3673
|
+
return denyByDefaultResolver;
|
|
3674
|
+
if (p3 === void 0 || p3 === "auto")
|
|
3675
|
+
return autoAllowResolver;
|
|
3676
|
+
return p3;
|
|
3677
|
+
}
|
|
3678
|
+
function finalText(events) {
|
|
3679
|
+
for (let i2 = events.length - 1; i2 >= 0; i2 -= 1) {
|
|
3680
|
+
const e3 = events[i2];
|
|
3681
|
+
if (e3.type === "assistant_message")
|
|
3682
|
+
return e3.content;
|
|
3683
|
+
}
|
|
3684
|
+
return "";
|
|
3685
|
+
}
|
|
3686
|
+
function mergePresets(presets) {
|
|
3687
|
+
const plugins = [];
|
|
3688
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3689
|
+
const tools = [];
|
|
3690
|
+
let provider;
|
|
3691
|
+
let cwd2;
|
|
3692
|
+
let permissions;
|
|
3693
|
+
let session;
|
|
3694
|
+
for (const p3 of presets) {
|
|
3695
|
+
for (const pl of p3.plugins ?? []) {
|
|
3696
|
+
if (seen.has(pl.name))
|
|
3697
|
+
continue;
|
|
3698
|
+
seen.add(pl.name);
|
|
3699
|
+
plugins.push(pl);
|
|
3700
|
+
}
|
|
3701
|
+
if (p3.tools)
|
|
3702
|
+
tools.push(...p3.tools);
|
|
3703
|
+
if (!provider && p3.provider)
|
|
3704
|
+
provider = p3.provider;
|
|
3705
|
+
cwd2 ??= p3.cwd;
|
|
3706
|
+
permissions ??= p3.permissions;
|
|
3707
|
+
if (p3.session)
|
|
3708
|
+
session = { ...session, ...p3.session };
|
|
3709
|
+
}
|
|
3710
|
+
return { plugins, tools, provider, cwd: cwd2, permissions, session };
|
|
3711
|
+
}
|
|
3712
|
+
function setupAgent(input = {}) {
|
|
3713
|
+
const opts = Array.isArray(input) ? mergePresets(input) : input;
|
|
3714
|
+
const session = new Session({
|
|
3715
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
3716
|
+
permissionResolver: resolvePermissions(opts.permissions),
|
|
3717
|
+
...opts.session
|
|
3718
|
+
});
|
|
3719
|
+
for (const plugin5 of opts.plugins ?? [])
|
|
3720
|
+
session.pluginHost.registerStatic(plugin5);
|
|
3721
|
+
for (const tool of opts.tools ?? [])
|
|
3722
|
+
session.tools.register(tool);
|
|
3723
|
+
if (opts.provider)
|
|
3724
|
+
session.providers.setActive(opts.provider.name, opts.provider.config);
|
|
3725
|
+
const agent = {
|
|
3726
|
+
session,
|
|
3727
|
+
async *stream(prompt, runOpts) {
|
|
3728
|
+
yield* runTurn(session, prompt, runOpts);
|
|
3729
|
+
},
|
|
3730
|
+
async ask(prompt, runOpts) {
|
|
3731
|
+
return finalText(await collectTurn(session, prompt, runOpts));
|
|
3732
|
+
},
|
|
3733
|
+
collect(prompt, runOpts) {
|
|
3734
|
+
return collectTurn(session, prompt, runOpts);
|
|
3735
|
+
},
|
|
3736
|
+
async discover() {
|
|
3737
|
+
await session.pluginHost.discoverAndLoad();
|
|
3738
|
+
return agent;
|
|
3739
|
+
},
|
|
3740
|
+
use(plugin5) {
|
|
3741
|
+
session.pluginHost.registerStatic(plugin5);
|
|
3742
|
+
return agent;
|
|
3743
|
+
},
|
|
3744
|
+
addTool(tool) {
|
|
3745
|
+
session.tools.register(tool);
|
|
3746
|
+
return agent;
|
|
3747
|
+
},
|
|
3748
|
+
removeTool(name) {
|
|
3749
|
+
session.tools.unregister(name);
|
|
3750
|
+
return agent;
|
|
3751
|
+
},
|
|
3752
|
+
setProvider(name, config) {
|
|
3753
|
+
session.providers.setActive(name, config);
|
|
3754
|
+
return agent;
|
|
3755
|
+
},
|
|
3756
|
+
setMode(name) {
|
|
3757
|
+
session.modes.setActive(name);
|
|
3758
|
+
return agent;
|
|
3759
|
+
}
|
|
3760
|
+
};
|
|
3761
|
+
return agent;
|
|
3762
|
+
}
|
|
3763
|
+
var init_setup_agent = __esm({
|
|
3764
|
+
"../core/dist/setup-agent.js"() {
|
|
3765
|
+
init_session();
|
|
3766
|
+
init_run_turn();
|
|
3767
|
+
init_resolvers();
|
|
3768
|
+
}
|
|
3769
|
+
});
|
|
3669
3770
|
function preferencesPath() {
|
|
3670
3771
|
return path3.join(os5.homedir(), ".moxxy", "preferences.json");
|
|
3671
3772
|
}
|
|
@@ -4149,6 +4250,45 @@ async function restoreEvents(sessionId, dir = defaultSessionsDir(), logger = cre
|
|
|
4149
4250
|
}
|
|
4150
4251
|
return events;
|
|
4151
4252
|
}
|
|
4253
|
+
async function readEventPage(sessionId, opts, dir = defaultSessionsDir()) {
|
|
4254
|
+
const logPath = path3.join(dir, `${sessionId}.jsonl`);
|
|
4255
|
+
let raw;
|
|
4256
|
+
try {
|
|
4257
|
+
raw = await promises.readFile(logPath, "utf8");
|
|
4258
|
+
} catch {
|
|
4259
|
+
return { events: [], prevCursor: null };
|
|
4260
|
+
}
|
|
4261
|
+
const all = [];
|
|
4262
|
+
for (const line of raw.split("\n")) {
|
|
4263
|
+
if (!line.trim())
|
|
4264
|
+
continue;
|
|
4265
|
+
try {
|
|
4266
|
+
all.push(JSON.parse(line));
|
|
4267
|
+
} catch {
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
return pageEvents(all, opts.before, opts.limit);
|
|
4271
|
+
}
|
|
4272
|
+
function pageEvents(events, before, limit2) {
|
|
4273
|
+
const cap = Math.max(0, Math.floor(limit2));
|
|
4274
|
+
if (cap === 0 || events.length === 0) {
|
|
4275
|
+
return { events: [], prevCursor: events.length === 0 ? null : before };
|
|
4276
|
+
}
|
|
4277
|
+
let end = events.length;
|
|
4278
|
+
if (before !== null) {
|
|
4279
|
+
end = 0;
|
|
4280
|
+
for (let i2 = 0; i2 < events.length; i2 += 1) {
|
|
4281
|
+
if (events[i2].seq < before)
|
|
4282
|
+
end = i2 + 1;
|
|
4283
|
+
else
|
|
4284
|
+
break;
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
const start = Math.max(0, end - cap);
|
|
4288
|
+
const page = events.slice(start, end);
|
|
4289
|
+
const prevCursor = start <= 0 ? null : page[0].seq;
|
|
4290
|
+
return { events: page, prevCursor };
|
|
4291
|
+
}
|
|
4152
4292
|
async function deleteSession(sessionId, dir = defaultSessionsDir()) {
|
|
4153
4293
|
await promises.rm(path3.join(dir, `${sessionId}.jsonl`), { force: true });
|
|
4154
4294
|
await promises.rm(metaPath(dir, sessionId), { force: true });
|
|
@@ -8414,16 +8554,19 @@ __export(dist_exports, {
|
|
|
8414
8554
|
mergeUsageStats: () => mergeUsageStats,
|
|
8415
8555
|
newSessionId: () => newSessionId,
|
|
8416
8556
|
newTurnId: () => newTurnId,
|
|
8557
|
+
pageEvents: () => pageEvents,
|
|
8417
8558
|
parseFrontmatter: () => parseFrontmatter,
|
|
8418
8559
|
parseSkillFile: () => parseSkillFile,
|
|
8419
8560
|
parseView: () => parseView,
|
|
8420
8561
|
permissionPolicySchema: () => permissionPolicySchema,
|
|
8421
8562
|
preferencesPath: () => preferencesPath,
|
|
8422
8563
|
readPackageMoxxyRequirements: () => readPackageMoxxyRequirements,
|
|
8564
|
+
readSessionEventPage: () => readEventPage,
|
|
8423
8565
|
readSessionIndex: () => readIndex,
|
|
8424
8566
|
restoreSessionEvents: () => restoreEvents,
|
|
8425
8567
|
runTurn: () => runTurn,
|
|
8426
8568
|
savePreferences: () => savePreferences,
|
|
8569
|
+
setupAgent: () => setupAgent,
|
|
8427
8570
|
silentLogger: () => silentLogger,
|
|
8428
8571
|
synthesizeSkill: () => synthesizeSkill,
|
|
8429
8572
|
toposortPluginManifests: () => toposortPluginManifests,
|
|
@@ -8434,6 +8577,7 @@ var init_dist = __esm({
|
|
|
8434
8577
|
"../core/dist/index.js"() {
|
|
8435
8578
|
init_session();
|
|
8436
8579
|
init_run_turn();
|
|
8580
|
+
init_setup_agent();
|
|
8437
8581
|
init_subagents();
|
|
8438
8582
|
init_preferences();
|
|
8439
8583
|
init_usage_stats();
|
|
@@ -136266,7 +136410,7 @@ async function reclaimStaleSocket(socketPath) {
|
|
|
136266
136410
|
|
|
136267
136411
|
// ../runner/dist/server.js
|
|
136268
136412
|
init_dist();
|
|
136269
|
-
var RUNNER_PROTOCOL_VERSION =
|
|
136413
|
+
var RUNNER_PROTOCOL_VERSION = 10;
|
|
136270
136414
|
var MIN_COMPATIBLE_PROTOCOL_VERSION = 1;
|
|
136271
136415
|
var RunnerMethod = {
|
|
136272
136416
|
/** client->server: handshake; returns the initial info snapshot. */
|
|
@@ -136285,6 +136429,13 @@ var RunnerMethod = {
|
|
|
136285
136429
|
* attached clients so every mirror clears in lockstep.
|
|
136286
136430
|
*/
|
|
136287
136431
|
SessionReset: "session.reset",
|
|
136432
|
+
/**
|
|
136433
|
+
* client->server: page the runner's authoritative event history (v10).
|
|
136434
|
+
* `{ before, limit }` → `{ events, prevCursor }`, newest-first. Backs the
|
|
136435
|
+
* desktop dual-history retirement; a v10 client gates it on the server's
|
|
136436
|
+
* reported version and falls back to its NDJSON store against an older runner.
|
|
136437
|
+
*/
|
|
136438
|
+
SessionLoadHistory: "session.loadHistory",
|
|
136288
136439
|
/** client->server: declare which resolvers this client will answer. */
|
|
136289
136440
|
SetResolver: "setResolver",
|
|
136290
136441
|
/** client->server: switch the active mode. */
|
|
@@ -136410,6 +136561,11 @@ var modeSetActiveParamsSchema = z.object({ name: z.string() });
|
|
|
136410
136561
|
var sessionSetReasoningParamsSchema = z.object({
|
|
136411
136562
|
effort: z.enum(["off", "low", "medium", "high"])
|
|
136412
136563
|
});
|
|
136564
|
+
var MAX_HISTORY_PAGE_LIMIT = 2e3;
|
|
136565
|
+
var sessionLoadHistoryParamsSchema = z.object({
|
|
136566
|
+
before: z.number().int().nonnegative().nullable(),
|
|
136567
|
+
limit: z.number().int().positive().max(MAX_HISTORY_PAGE_LIMIT)
|
|
136568
|
+
});
|
|
136413
136569
|
var providerSetActiveParamsSchema = z.object({
|
|
136414
136570
|
name: z.string(),
|
|
136415
136571
|
config: z.record(z.unknown()).optional()
|
|
@@ -136734,6 +136890,7 @@ async function handleSurfaceClose(ctx, raw) {
|
|
|
136734
136890
|
}
|
|
136735
136891
|
|
|
136736
136892
|
// ../runner/dist/handlers/session-handlers.js
|
|
136893
|
+
init_dist();
|
|
136737
136894
|
function handleModeSetActive(ctx, raw) {
|
|
136738
136895
|
const { name } = modeSetActiveParamsSchema.parse(raw);
|
|
136739
136896
|
ctx.session.modes.setActive(name);
|
|
@@ -136745,6 +136902,14 @@ function handleSessionSetReasoning(ctx, raw) {
|
|
|
136745
136902
|
ctx.broadcastInfo();
|
|
136746
136903
|
return {};
|
|
136747
136904
|
}
|
|
136905
|
+
async function handleSessionLoadHistory(ctx, raw) {
|
|
136906
|
+
const { before, limit: limit2 } = sessionLoadHistoryParamsSchema.parse(raw);
|
|
136907
|
+
const log = ctx.session.log;
|
|
136908
|
+
if (log.baseSeq === 0) {
|
|
136909
|
+
return pageEvents(log.toJSON(), before, limit2);
|
|
136910
|
+
}
|
|
136911
|
+
return readEventPage(String(ctx.session.id), { before, limit: limit2 }, ctx.sessionsDir);
|
|
136912
|
+
}
|
|
136748
136913
|
async function handlePermissionAddAllow(ctx, raw) {
|
|
136749
136914
|
const { name, reason } = permissionAddAllowParamsSchema.parse(raw);
|
|
136750
136915
|
await ctx.session.permissions.addAllow({ name, ...reason ? { reason } : {} });
|
|
@@ -136863,6 +137028,7 @@ var RunnerServer = class {
|
|
|
136863
137028
|
peer.handle(RunnerMethod.RunTurn, (raw) => this.handleRunTurn(client, raw));
|
|
136864
137029
|
peer.handle(RunnerMethod.Abort, (raw) => this.handleAbort(client, raw));
|
|
136865
137030
|
peer.handle(RunnerMethod.SessionReset, () => this.handleSessionReset());
|
|
137031
|
+
peer.handle(RunnerMethod.SessionLoadHistory, (raw) => handleSessionLoadHistory(ctx, raw));
|
|
136866
137032
|
peer.handle(RunnerMethod.SetResolver, (raw) => this.handleSetResolver(client, raw));
|
|
136867
137033
|
peer.handle(RunnerMethod.ModeSetActive, (raw) => handleModeSetActive(ctx, raw));
|
|
136868
137034
|
peer.handle(RunnerMethod.SessionSetReasoning, (raw) => handleSessionSetReasoning(ctx, raw));
|
|
@@ -136946,6 +137112,7 @@ var RunnerServer = class {
|
|
|
136946
137112
|
} finally {
|
|
136947
137113
|
this.turnControllers.delete(turnId);
|
|
136948
137114
|
client.turns.delete(turnId);
|
|
137115
|
+
await this.sealUnsealedStreamedText(turnId);
|
|
136949
137116
|
this.broadcast(RunnerNotification.TurnComplete, {
|
|
136950
137117
|
turnId,
|
|
136951
137118
|
...error2 ? { error: error2 } : {}
|
|
@@ -136990,6 +137157,59 @@ var RunnerServer = class {
|
|
|
136990
137157
|
this.session.log.clear();
|
|
136991
137158
|
return {};
|
|
136992
137159
|
}
|
|
137160
|
+
/**
|
|
137161
|
+
* If this turn streamed assistant text (`assistant_chunk`) that no
|
|
137162
|
+
* `assistant_message` ever sealed, append a REAL `assistant_message` to the
|
|
137163
|
+
* authoritative log so it persists + replays like any other reply. It
|
|
137164
|
+
* accumulates chunk deltas and seals whatever text remains at turn end — so a
|
|
137165
|
+
* cleanly sealed reply (the normal path) leaves an empty remainder and this is
|
|
137166
|
+
* a no-op.
|
|
137167
|
+
*
|
|
137168
|
+
* The accumulator resets on BOTH per-iteration boundaries — an
|
|
137169
|
+
* `assistant_message` (a reply was sealed) AND a `provider_request` (a fresh
|
|
137170
|
+
* provider iteration begins). Resetting on `provider_request` is what scopes
|
|
137171
|
+
* the seal to the FINAL iteration: a retryable provider error can abandon a
|
|
137172
|
+
* partially-streamed iteration WITHOUT sealing it (the mode loop emits the
|
|
137173
|
+
* error and `continue`s), and a later iteration then streams fresh text and
|
|
137174
|
+
* may itself end unsealed (a fatal error / abort / max-iterations). Without
|
|
137175
|
+
* the `provider_request` reset, the abandoned attempt's chunks would be
|
|
137176
|
+
* concatenated INTO the sealed reply ("ABANDONED-final" instead of "final"),
|
|
137177
|
+
* durably corrupting authoritative/replayed history. (This is a deliberate
|
|
137178
|
+
* improvement over the desktop renderer's old turn-complete synthesis, which
|
|
137179
|
+
* accumulated across iterations and had exactly that defect — and which this
|
|
137180
|
+
* seal retires.)
|
|
137181
|
+
*
|
|
137182
|
+
* The append flows through `session.log` → persistence + the broadcast stream,
|
|
137183
|
+
* so mirrors ingest it as a normal event and never need to synthesize their
|
|
137184
|
+
* own.
|
|
137185
|
+
*/
|
|
137186
|
+
async sealUnsealedStreamedText(turnId) {
|
|
137187
|
+
const events = this.session.log.byTurn(turnId);
|
|
137188
|
+
if (events.length === 0)
|
|
137189
|
+
return;
|
|
137190
|
+
let unsealed = "";
|
|
137191
|
+
for (const event of events) {
|
|
137192
|
+
if (event.type === "assistant_message" || event.type === "provider_request")
|
|
137193
|
+
unsealed = "";
|
|
137194
|
+
else if (event.type === "assistant_chunk")
|
|
137195
|
+
unsealed += event.delta;
|
|
137196
|
+
}
|
|
137197
|
+
if (!unsealed.trim())
|
|
137198
|
+
return;
|
|
137199
|
+
try {
|
|
137200
|
+
await this.session.log.append({
|
|
137201
|
+
type: "assistant_message",
|
|
137202
|
+
sessionId: this.session.id,
|
|
137203
|
+
turnId,
|
|
137204
|
+
source: "model",
|
|
137205
|
+
content: unsealed,
|
|
137206
|
+
// The turn ended without the provider sealing the message (error/abort
|
|
137207
|
+
// after partial text); record it as a normal completed reply.
|
|
137208
|
+
stopReason: "end_turn"
|
|
137209
|
+
});
|
|
137210
|
+
} catch {
|
|
137211
|
+
}
|
|
137212
|
+
}
|
|
136993
137213
|
handleSetResolver(client, raw) {
|
|
136994
137214
|
const params = setResolverParamsSchema.parse(raw);
|
|
136995
137215
|
if (params.permission !== void 0)
|
|
@@ -137602,6 +137822,26 @@ var RemoteSession = class {
|
|
|
137602
137822
|
async reset() {
|
|
137603
137823
|
await this.peer.request(RunnerMethod.SessionReset, {});
|
|
137604
137824
|
}
|
|
137825
|
+
/**
|
|
137826
|
+
* Page the runner's AUTHORITATIVE event history (protocol v10). Backs the
|
|
137827
|
+
* desktop's dual-history retirement — the renderer reads transcript history
|
|
137828
|
+
* from the runner instead of its own NDJSON chat store. Newest-first paging:
|
|
137829
|
+
* pass `before: null` for the newest page, then feed each result's
|
|
137830
|
+
* `prevCursor` back as `before` to walk older pages until `prevCursor` is
|
|
137831
|
+
* `null` (start of history).
|
|
137832
|
+
*
|
|
137833
|
+
* GATED on the server reporting protocol v10+. Against an OLDER runner this
|
|
137834
|
+
* throws a clear, actionable error (not a raw method-not-found) — the desktop
|
|
137835
|
+
* CATCHES it and falls back to its existing NDJSON path, so no transcript
|
|
137836
|
+
* ever goes blank when the runner predates this method.
|
|
137837
|
+
*/
|
|
137838
|
+
async loadHistory(before, limit2) {
|
|
137839
|
+
this.requireServerProtocol(10, "Loading session history from the runner");
|
|
137840
|
+
return this.peer.request(RunnerMethod.SessionLoadHistory, {
|
|
137841
|
+
before,
|
|
137842
|
+
limit: limit2
|
|
137843
|
+
});
|
|
137844
|
+
}
|
|
137605
137845
|
getInfo() {
|
|
137606
137846
|
return this.requireInfo();
|
|
137607
137847
|
}
|
|
@@ -138418,7 +138658,7 @@ var CollaborationState = class {
|
|
|
138418
138658
|
return;
|
|
138419
138659
|
agent.status = status;
|
|
138420
138660
|
this.emitFn({ kind: "agent_status", agentId, status, ...detail ? { detail } : {} });
|
|
138421
|
-
if (status === "crashed" || status === "killed")
|
|
138661
|
+
if (status === "crashed" || status === "killed" || status === "failed")
|
|
138422
138662
|
this.releaseAllFor(agentId);
|
|
138423
138663
|
}
|
|
138424
138664
|
markDone(agentId, summary, artifacts) {
|
|
@@ -138431,7 +138671,7 @@ var CollaborationState = class {
|
|
|
138431
138671
|
this.emitFn({ kind: "agent_done", agentId, summary, ...artifacts ? { artifacts } : {} });
|
|
138432
138672
|
}
|
|
138433
138673
|
allDone() {
|
|
138434
|
-
const live = this.agentOrder.map((id) => this.agents.get(id)).filter((a2) => a2.status !== "crashed" && a2.status !== "killed");
|
|
138674
|
+
const live = this.agentOrder.map((id) => this.agents.get(id)).filter((a2) => a2.status !== "crashed" && a2.status !== "killed" && a2.status !== "failed");
|
|
138435
138675
|
return live.length > 0 && live.every((a2) => a2.status === "done");
|
|
138436
138676
|
}
|
|
138437
138677
|
roleOf(agentId) {
|
|
@@ -138789,7 +139029,7 @@ async function createCollaborationHub(opts) {
|
|
|
138789
139029
|
connections.delete(peer);
|
|
138790
139030
|
if (agentId) {
|
|
138791
139031
|
const agent = state.rosterView().agents.find((a2) => a2.id === agentId);
|
|
138792
|
-
if (agent && agent.status !== "done" && agent.status !== "killed") {
|
|
139032
|
+
if (agent && agent.status !== "done" && agent.status !== "failed" && agent.status !== "killed") {
|
|
138793
139033
|
state.setStatus(agentId, "crashed");
|
|
138794
139034
|
}
|
|
138795
139035
|
}
|
|
@@ -139035,13 +139275,24 @@ var PeerSupervisor = class {
|
|
|
139035
139275
|
child.on("exit", () => {
|
|
139036
139276
|
proc.exited = true;
|
|
139037
139277
|
});
|
|
139278
|
+
child.on("error", (err) => {
|
|
139279
|
+
proc.exited = true;
|
|
139280
|
+
proc.stderr.push(`spawn error: ${err.message}`);
|
|
139281
|
+
});
|
|
139038
139282
|
return { socket };
|
|
139039
139283
|
}
|
|
139284
|
+
/** True once the child has exited or its spawn failed. */
|
|
139285
|
+
hasExited(agentId) {
|
|
139286
|
+
const proc = this.peers.get(agentId);
|
|
139287
|
+
return proc ? proc.exited : false;
|
|
139288
|
+
}
|
|
139040
139289
|
/** Last stderr lines from a peer — used to diagnose a crash. */
|
|
139041
139290
|
stderrOf(agentId) {
|
|
139042
139291
|
return this.peers.get(agentId)?.stderr ?? [];
|
|
139043
139292
|
}
|
|
139044
|
-
/**
|
|
139293
|
+
/** Stop a single peer and AWAIT its real exit (with a force-kill fallback), so
|
|
139294
|
+
* callers — e.g. the sequential fallback — can rely on the workspace being
|
|
139295
|
+
* free before the next agent starts. */
|
|
139045
139296
|
async stop(agentId) {
|
|
139046
139297
|
const proc = this.peers.get(agentId);
|
|
139047
139298
|
if (!proc || proc.exited)
|
|
@@ -139050,6 +139301,27 @@ var PeerSupervisor = class {
|
|
|
139050
139301
|
proc.child.kill("SIGTERM");
|
|
139051
139302
|
} catch {
|
|
139052
139303
|
}
|
|
139304
|
+
await new Promise((resolve13) => {
|
|
139305
|
+
if (proc.exited)
|
|
139306
|
+
return resolve13();
|
|
139307
|
+
let settled = false;
|
|
139308
|
+
const done = () => {
|
|
139309
|
+
if (settled)
|
|
139310
|
+
return;
|
|
139311
|
+
settled = true;
|
|
139312
|
+
clearTimeout(timer);
|
|
139313
|
+
resolve13();
|
|
139314
|
+
};
|
|
139315
|
+
proc.child.once("exit", done);
|
|
139316
|
+
const timer = setTimeout(() => {
|
|
139317
|
+
try {
|
|
139318
|
+
proc.child.kill("SIGKILL");
|
|
139319
|
+
} catch {
|
|
139320
|
+
}
|
|
139321
|
+
done();
|
|
139322
|
+
}, FORCE_KILL_GRACE_MS);
|
|
139323
|
+
timer.unref?.();
|
|
139324
|
+
});
|
|
139053
139325
|
}
|
|
139054
139326
|
async shutdownAll(_reason) {
|
|
139055
139327
|
if (this.shuttingDown)
|
|
@@ -139183,6 +139455,7 @@ function releaseCollabLock(sessionId) {
|
|
|
139183
139455
|
|
|
139184
139456
|
// ../mode-collaborative/dist/collab-loop.js
|
|
139185
139457
|
var POLL_MS = 500;
|
|
139458
|
+
var BOOT_DEADLINE_MS = 9e4;
|
|
139186
139459
|
function runCollaborativeMode(ctx) {
|
|
139187
139460
|
return runCollaborative(ctx, {});
|
|
139188
139461
|
}
|
|
@@ -139251,13 +139524,18 @@ async function* runCollaborative(ctx, deps) {
|
|
|
139251
139524
|
yield await ctx.emit(plugin4(ctx, "collab_started", { task, parallel, gitInstalled: gitInstalled2, gitRepo }));
|
|
139252
139525
|
supervisor.spawn({ entry: architectEntry, cwd: cwd2, mode: COLLAB_ARCHITECT_MODE_NAME });
|
|
139253
139526
|
yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: ARCHITECT_AGENT_ID, role: "architect" }));
|
|
139254
|
-
const architectOk = await waitForAgent(hub, ARCHITECT_AGENT_ID, ctx.signal, cfg.wallClockMs);
|
|
139527
|
+
const architectOk = await waitForAgent(hub, supervisor, ARCHITECT_AGENT_ID, ctx.signal, cfg.wallClockMs);
|
|
139255
139528
|
if (ctx.signal.aborted) {
|
|
139256
139529
|
yield await ctx.emit(emitAbort(ctx, "aborted during design"));
|
|
139257
139530
|
return;
|
|
139258
139531
|
}
|
|
139259
139532
|
if (!architectOk) {
|
|
139260
|
-
|
|
139533
|
+
const why = supervisor.stderrOf(ARCHITECT_AGENT_ID).slice(-4).join("\n");
|
|
139534
|
+
yield await ctx.emit(plugin4(ctx, "collab_agent_failed", { id: ARCHITECT_AGENT_ID, status: statusOf(hub, ARCHITECT_AGENT_ID), stderr: supervisor.stderrOf(ARCHITECT_AGENT_ID).slice(-6) }));
|
|
139535
|
+
yield await ctx.emit(assistant(ctx, `The architect did not finish the design \u2014 stopping the collaboration.${why ? `
|
|
139536
|
+
|
|
139537
|
+
Last diagnostics:
|
|
139538
|
+
${why}` : ""}`));
|
|
139261
139539
|
return;
|
|
139262
139540
|
}
|
|
139263
139541
|
let roster = readRoster(join(cwd2, COLLAB_SCAFFOLD_DIR, ROSTER_FILENAME), cfg.maxAgents);
|
|
@@ -139298,7 +139576,8 @@ async function* runCollaborative(ctx, deps) {
|
|
|
139298
139576
|
supervisor.spawn({ entry, cwd: wt3, mode: COLLAB_PEER_MODE_NAME });
|
|
139299
139577
|
yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: entry.id, role: entry.role }));
|
|
139300
139578
|
}
|
|
139301
|
-
await waitForAgents(hub, roster.map((r2) => r2.id), ctx.signal, cfg.wallClockMs);
|
|
139579
|
+
await waitForAgents(hub, supervisor, roster.map((r2) => r2.id), ctx.signal, cfg.wallClockMs);
|
|
139580
|
+
yield* surfaceFailures(ctx, hub, supervisor, roster.map((r2) => r2.id));
|
|
139302
139581
|
for (const r2 of roster)
|
|
139303
139582
|
if (statusOf(hub, r2.id) === "done")
|
|
139304
139583
|
doneIds.push(r2.id);
|
|
@@ -139309,7 +139588,9 @@ async function* runCollaborative(ctx, deps) {
|
|
|
139309
139588
|
hub.state.addAgent(entry);
|
|
139310
139589
|
supervisor.spawn({ entry, cwd: cwd2, mode: COLLAB_PEER_MODE_NAME });
|
|
139311
139590
|
yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: entry.id, role: entry.role }));
|
|
139312
|
-
const ok = await waitForAgent(hub, entry.id, ctx.signal, cfg.wallClockMs);
|
|
139591
|
+
const ok = await waitForAgent(hub, supervisor, entry.id, ctx.signal, cfg.wallClockMs);
|
|
139592
|
+
if (!ok)
|
|
139593
|
+
yield* surfaceFailures(ctx, hub, supervisor, [entry.id]);
|
|
139313
139594
|
await supervisor.stop(entry.id);
|
|
139314
139595
|
if (ok && statusOf(hub, entry.id) === "done")
|
|
139315
139596
|
doneIds.push(entry.id);
|
|
@@ -139353,6 +139634,16 @@ ${mergeNote}` : ""}`));
|
|
|
139353
139634
|
if (hub)
|
|
139354
139635
|
await hub.close();
|
|
139355
139636
|
releaseCollabLock(String(ctx.sessionId));
|
|
139637
|
+
for (const wt3 of worktrees.values()) {
|
|
139638
|
+
await removeWorktree(cwd2, wt3).catch(() => void 0);
|
|
139639
|
+
}
|
|
139640
|
+
try {
|
|
139641
|
+
rmSync(collabRunDir(runId), { recursive: true, force: true });
|
|
139642
|
+
rmSync(worktreeRoot(runId), { recursive: true, force: true });
|
|
139643
|
+
} catch {
|
|
139644
|
+
}
|
|
139645
|
+
if (worktrees.size > 0)
|
|
139646
|
+
await git(cwd2, ["worktree", "prune"]).catch(() => void 0);
|
|
139356
139647
|
}
|
|
139357
139648
|
}
|
|
139358
139649
|
function lastUserPromptText(ctx) {
|
|
@@ -139403,30 +139694,62 @@ function slug(s2) {
|
|
|
139403
139694
|
function statusOf(hub, id) {
|
|
139404
139695
|
return hub.state.rosterView().agents.find((a2) => a2.id === id)?.status;
|
|
139405
139696
|
}
|
|
139406
|
-
|
|
139407
|
-
const
|
|
139697
|
+
function agentSettled(hub, supervisor, id, connected, bootDeadlineAt) {
|
|
139698
|
+
const status = statusOf(hub, id);
|
|
139699
|
+
if (status && status !== "pending")
|
|
139700
|
+
connected.add(id);
|
|
139701
|
+
if (status === "done")
|
|
139702
|
+
return "done";
|
|
139703
|
+
if (status === "failed" || status === "crashed" || status === "killed")
|
|
139704
|
+
return "failed";
|
|
139705
|
+
if (supervisor?.hasExited(id))
|
|
139706
|
+
return "failed";
|
|
139707
|
+
if (!connected.has(id) && Date.now() > bootDeadlineAt)
|
|
139708
|
+
return "failed";
|
|
139709
|
+
return void 0;
|
|
139710
|
+
}
|
|
139711
|
+
async function waitForAgent(hub, supervisor, id, signal, wallClockMs) {
|
|
139712
|
+
const wallDeadline = Date.now() + wallClockMs;
|
|
139713
|
+
const bootDeadlineAt = Date.now() + BOOT_DEADLINE_MS;
|
|
139714
|
+
const connected = /* @__PURE__ */ new Set();
|
|
139408
139715
|
for (; ; ) {
|
|
139409
|
-
const
|
|
139410
|
-
if (
|
|
139716
|
+
const settled = agentSettled(hub, supervisor, id, connected, bootDeadlineAt);
|
|
139717
|
+
if (settled === "done")
|
|
139411
139718
|
return true;
|
|
139412
|
-
if (
|
|
139719
|
+
if (settled === "failed")
|
|
139413
139720
|
return false;
|
|
139414
|
-
if (signal.aborted || Date.now() >
|
|
139721
|
+
if (signal.aborted || Date.now() > wallDeadline)
|
|
139415
139722
|
return false;
|
|
139416
139723
|
await sleep5(POLL_MS, signal);
|
|
139417
139724
|
}
|
|
139418
139725
|
}
|
|
139419
|
-
async function waitForAgents(hub, ids, signal,
|
|
139420
|
-
const
|
|
139421
|
-
const
|
|
139726
|
+
async function waitForAgents(hub, supervisor, ids, signal, wallClockMs) {
|
|
139727
|
+
const wallDeadline = Date.now() + wallClockMs;
|
|
139728
|
+
const bootDeadlineAt = Date.now() + BOOT_DEADLINE_MS;
|
|
139729
|
+
const connected = /* @__PURE__ */ new Set();
|
|
139422
139730
|
for (; ; ) {
|
|
139423
|
-
if (ids.every((id) =>
|
|
139731
|
+
if (ids.every((id) => agentSettled(hub, supervisor, id, connected, bootDeadlineAt) !== void 0))
|
|
139424
139732
|
return;
|
|
139425
|
-
if (signal.aborted || Date.now() >
|
|
139733
|
+
if (signal.aborted || Date.now() > wallDeadline)
|
|
139426
139734
|
return;
|
|
139427
139735
|
await sleep5(POLL_MS, signal);
|
|
139428
139736
|
}
|
|
139429
139737
|
}
|
|
139738
|
+
async function* surfaceFailures(ctx, hub, supervisor, ids) {
|
|
139739
|
+
for (const id of ids) {
|
|
139740
|
+
const status = statusOf(hub, id);
|
|
139741
|
+
if (status === "done")
|
|
139742
|
+
continue;
|
|
139743
|
+
if (status !== "failed" && status !== "crashed" && status !== "killed") {
|
|
139744
|
+
hub.state.setStatus(id, "crashed", "did not reach a terminal status");
|
|
139745
|
+
}
|
|
139746
|
+
yield await ctx.emit(plugin4(ctx, "collab_agent_failed", {
|
|
139747
|
+
id,
|
|
139748
|
+
status: statusOf(hub, id),
|
|
139749
|
+
stderr: supervisor.stderrOf(id).slice(-6)
|
|
139750
|
+
}));
|
|
139751
|
+
}
|
|
139752
|
+
}
|
|
139430
139753
|
function sleep5(ms, signal) {
|
|
139431
139754
|
return new Promise((resolve13) => {
|
|
139432
139755
|
if (signal.aborted)
|
|
@@ -139548,6 +139871,8 @@ async function* runCollabAgentLoop(ctx, opts) {
|
|
|
139548
139871
|
permissions: autoApprove
|
|
139549
139872
|
};
|
|
139550
139873
|
const hub = await getProcessHubClient();
|
|
139874
|
+
if (hub)
|
|
139875
|
+
await hub.setStatus("working").catch(() => void 0);
|
|
139551
139876
|
const detector = createStuckLoopDetector();
|
|
139552
139877
|
const maxIterations = ctx.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
139553
139878
|
let noop3 = 0;
|
|
@@ -144150,12 +144475,9 @@ var REMOTE_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
144150
144475
|
// Voice input (capability-probed; transcribe fails coded without a transcriber).
|
|
144151
144476
|
"session.hasTranscriber",
|
|
144152
144477
|
"session.transcribe",
|
|
144153
|
-
//
|
|
144154
|
-
//
|
|
144155
|
-
"chat.
|
|
144156
|
-
"chat.loadSegment",
|
|
144157
|
-
"chat.clearLog",
|
|
144158
|
-
"chat.migrate",
|
|
144478
|
+
// Read a workspace's transcript history from the runner's authoritative log
|
|
144479
|
+
// (a paired phone may read history, scoped to a workspace, not host config).
|
|
144480
|
+
"chat.loadHistory",
|
|
144159
144481
|
// Workflows: READ + run an existing one only. Authoring (`workflows.save`,
|
|
144160
144482
|
// `workflows.validateDraft`, `workflows.setEnabled`) is host-only — a paired
|
|
144161
144483
|
// phone must not rewrite or re-enable the host's workflows.
|
|
@@ -144363,25 +144685,13 @@ var ipcInputSchemas = {
|
|
|
144363
144685
|
"mobileGateway.status": z.undefined(),
|
|
144364
144686
|
"mobileGateway.rotateToken": z.undefined(),
|
|
144365
144687
|
"mobileGateway.setEnabled": z.object({ enabled: z.boolean() }).strict(),
|
|
144366
|
-
|
|
144367
|
-
|
|
144368
|
-
|
|
144369
|
-
|
|
144370
|
-
"chat.loadSegment": z.object({
|
|
144688
|
+
// `before` is a runner `seq` cursor; the page is RAW events. The runner itself
|
|
144689
|
+
// re-validates and caps at its own MAX_HISTORY_PAGE_LIMIT (2000), so bound the
|
|
144690
|
+
// renderer's raw-window request to that ceiling.
|
|
144691
|
+
"chat.loadHistory": z.object({
|
|
144371
144692
|
workspaceId: z.string().min(1).max(256),
|
|
144372
144693
|
before: z.number().int().nonnegative().nullable(),
|
|
144373
|
-
limit: z.number().int().positive().max(
|
|
144374
|
-
}),
|
|
144375
|
-
"chat.clearLog": z.object({ workspaceId: z.string().min(1).max(256) }),
|
|
144376
|
-
// chat.migrate writes the supplied events straight into per-workspace NDJSON
|
|
144377
|
-
// logs on disk, so it's a filesystem-touching command: bound both the number
|
|
144378
|
-
// of workspaces and the events per workspace, and lock the workspaceId to a
|
|
144379
|
-
// non-empty bounded slug so it can't traverse out of the log directory.
|
|
144380
|
-
"chat.migrate": z.object({
|
|
144381
|
-
workspaces: z.array(z.object({
|
|
144382
|
-
workspaceId: z.string().min(1).max(256),
|
|
144383
|
-
events: z.array(z.unknown()).max(1e4)
|
|
144384
|
-
})).max(100)
|
|
144694
|
+
limit: z.number().int().positive().max(2e3)
|
|
144385
144695
|
}),
|
|
144386
144696
|
// Vault writes are security-sensitive: lock the key name to a safe slug
|
|
144387
144697
|
// (letters/digits + . _ / - , no traversal) and bound the secret size.
|
|
@@ -144626,13 +144936,7 @@ var MobileSessionHost = class {
|
|
|
144626
144936
|
this.bus.handle("ask.respond", async ({ requestId, response }) => {
|
|
144627
144937
|
this.answerAsk(requestId, response);
|
|
144628
144938
|
});
|
|
144629
|
-
this.bus.handle("chat.
|
|
144630
|
-
this.bus.handle("chat.append", async () => {
|
|
144631
|
-
});
|
|
144632
|
-
this.bus.handle("chat.clearLog", async () => {
|
|
144633
|
-
});
|
|
144634
|
-
this.bus.handle("chat.migrate", async () => {
|
|
144635
|
-
});
|
|
144939
|
+
this.bus.handle("chat.loadHistory", async () => ({ events: [], prevCursor: null }));
|
|
144636
144940
|
}
|
|
144637
144941
|
/** Stream session events to clients + install the ask resolvers. */
|
|
144638
144942
|
wire() {
|
|
@@ -145703,8 +146007,8 @@ function runProcess(cmd, args, opts) {
|
|
|
145703
146007
|
}
|
|
145704
146008
|
|
|
145705
146009
|
// ../plugin-browser/dist/browser-surface.js
|
|
145706
|
-
var FRAME_INTERVAL_MS =
|
|
145707
|
-
var FAIL_GRACE =
|
|
146010
|
+
var FRAME_INTERVAL_MS = 300;
|
|
146011
|
+
var FAIL_GRACE = 6;
|
|
145708
146012
|
function buildBrowserSurface(deps) {
|
|
145709
146013
|
return defineSurface({
|
|
145710
146014
|
kind: "browser",
|
|
@@ -145831,9 +146135,10 @@ function buildBrowserSurface(deps) {
|
|
|
145831
146135
|
} else if (msg.type === "move" && typeof msg.fx === "number" && typeof msg.fy === "number") {
|
|
145832
146136
|
await browserSidecarCall("mousemove", { x: msg.fx * vw, y: msg.fy * vh }, deps).catch(() => void 0);
|
|
145833
146137
|
void tick();
|
|
145834
|
-
} else if (msg.type === "
|
|
145835
|
-
const
|
|
145836
|
-
|
|
146138
|
+
} else if (msg.type === "capture" && typeof msg.fx === "number" && typeof msg.fy === "number" && typeof msg.fw === "number" && typeof msg.fh === "number") {
|
|
146139
|
+
const shot = await browserSidecarCall("capture", { x: msg.fx * vw, y: msg.fy * vh, width: msg.fw * vw, height: msg.fh * vh }, deps).catch(() => null);
|
|
146140
|
+
if (shot)
|
|
146141
|
+
emit2({ type: "captured", base64: shot.base64, mediaType: shot.mediaType });
|
|
145837
146142
|
} else if (msg.type === "zoom" && typeof msg.factor === "number") {
|
|
145838
146143
|
await browserSidecarCall("zoom", { factor: msg.factor }, deps).catch(() => void 0);
|
|
145839
146144
|
bump();
|
|
@@ -155668,6 +155973,16 @@ async function runAgentCommand(argv) {
|
|
|
155668
155973
|
for await (const _2 of session.runTurn(subtask)) void _2;
|
|
155669
155974
|
} catch {
|
|
155670
155975
|
}
|
|
155976
|
+
try {
|
|
155977
|
+
const hub = await getProcessHubClient();
|
|
155978
|
+
if (hub) {
|
|
155979
|
+
const mine = (await hub.roster()).agents.find((a2) => a2.id === hub.agentId);
|
|
155980
|
+
if (mine && mine.status !== "done") {
|
|
155981
|
+
await hub.setStatus("failed", "turn ended without calling collab_done");
|
|
155982
|
+
}
|
|
155983
|
+
}
|
|
155984
|
+
} catch {
|
|
155985
|
+
}
|
|
155671
155986
|
})();
|
|
155672
155987
|
await runUntilSignal2(runnerServer, session, turnDone);
|
|
155673
155988
|
return 0;
|