dsh-loop-engine 1.0.0-rc7 → 1.0.0-rc8
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 +19 -0
- package/lib/client.js +6 -2
- package/lib/index.js +832 -258
- package/lib/invariant.js +2 -1
- package/lib/types/client/LoopEngineBadge.d.ts +1 -1
- package/lib/types/client/LoopEngineComposerSelect.d.ts +1 -1
- package/lib/types/client/LoopEngineSection.d.ts +1 -1
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/store.d.ts +2 -1
- package/lib/types/engine-claude/agent.d.ts +2 -0
- package/lib/types/engine-claude/loop.d.ts +24 -2
- package/lib/types/engine-claude/mapping.d.ts +3 -3
- package/lib/types/engine-codex/agent.d.ts +2 -0
- package/lib/types/engine-codex/appserver/mapping.d.ts +4 -4
- package/lib/types/engine-codex/loop.d.ts +24 -2
- package/lib/types/engine-kimi/agent.d.ts +2 -0
- package/lib/types/engine-kimi/commands.d.ts +7 -0
- package/lib/types/engine-kimi/loop.d.ts +24 -2
- package/lib/types/engine-pi/agent.d.ts +2 -0
- package/lib/types/engine-pi/loop.d.ts +24 -2
- package/lib/types/index.d.ts +16 -0
- package/lib/types/patch-manager.d.ts +9 -0
- package/lib/types/preset.d.ts +73 -0
- package/lib/types/provider-route.d.ts +35 -0
- package/lib/types/settings.d.ts +5 -5
- package/package.json +23 -23
package/lib/index.js
CHANGED
|
@@ -46,11 +46,10 @@ var __callDispose = (stack, error, hasError) => {
|
|
|
46
46
|
|
|
47
47
|
// src/index.ts
|
|
48
48
|
import { mkdirSync, readFileSync as readFileSync3, renameSync, writeFileSync } from "node:fs";
|
|
49
|
-
import { mkdir, readFile as
|
|
50
|
-
import { randomUUID } from "node:crypto";
|
|
51
|
-
import { dirname as
|
|
49
|
+
import { mkdir as mkdir2, readFile as readFile6, rename as rename2, writeFile as writeFile2 } from "node:fs/promises";
|
|
50
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
51
|
+
import { dirname as dirname6, join as join11 } from "node:path";
|
|
52
52
|
import z6 from "@deepseek-ai/schemastery";
|
|
53
|
-
import { installSettingsSection } from "@deepseek-ai/dsh-settings";
|
|
54
53
|
import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
55
54
|
|
|
56
55
|
// src/engine-claude/loop.ts
|
|
@@ -58,7 +57,7 @@ import { Service } from "@deepseek-ai/cordis";
|
|
|
58
57
|
import z from "@deepseek-ai/schemastery";
|
|
59
58
|
import { emitAgentEvent } from "@deepseek-ai/dsh-agent";
|
|
60
59
|
import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
|
|
61
|
-
import { SessionPreparation } from "@deepseek-ai/dsh-session";
|
|
60
|
+
import { interruptedTurnClosers, SessionLogOffset, SessionPreparation } from "@deepseek-ai/dsh-session";
|
|
62
61
|
|
|
63
62
|
// src/engine-claude/agent.ts
|
|
64
63
|
import { Inbox, agentEvents } from "@deepseek-ai/dsh-agent";
|
|
@@ -69,7 +68,7 @@ import { query as officialQuery } from "@anthropic-ai/claude-agent-sdk";
|
|
|
69
68
|
|
|
70
69
|
// src/engine-claude/mapping.ts
|
|
71
70
|
import {
|
|
72
|
-
|
|
71
|
+
ToolCallId,
|
|
73
72
|
createToolResultMessage
|
|
74
73
|
} from "@deepseek-ai/dsh-llm";
|
|
75
74
|
function stringifyToolInput(input) {
|
|
@@ -88,7 +87,7 @@ function mapAssistantMessage(message) {
|
|
|
88
87
|
content.push({ type: "text", text: block.text });
|
|
89
88
|
break;
|
|
90
89
|
case "tool_use": {
|
|
91
|
-
const callId =
|
|
90
|
+
const callId = ToolCallId(block.id);
|
|
92
91
|
content.push({
|
|
93
92
|
type: "tool-call",
|
|
94
93
|
id: callId,
|
|
@@ -123,7 +122,7 @@ function mapToolResults(message) {
|
|
|
123
122
|
for (const block of content) {
|
|
124
123
|
if (block.type !== "tool_result") continue;
|
|
125
124
|
results.push(createToolResultMessage({
|
|
126
|
-
callId:
|
|
125
|
+
callId: ToolCallId(block.tool_use_id),
|
|
127
126
|
content: toolResultContent(block.content),
|
|
128
127
|
isError: block.is_error === true
|
|
129
128
|
}));
|
|
@@ -166,7 +165,7 @@ function mapStreamEvent(event, toolCalls) {
|
|
|
166
165
|
return [{ type: "block-start", index: event.index, blockType: "reasoning" }];
|
|
167
166
|
}
|
|
168
167
|
if (block.type === "tool_use") {
|
|
169
|
-
toolCalls.set(event.index, { callId:
|
|
168
|
+
toolCalls.set(event.index, { callId: ToolCallId(block.id), name: block.name });
|
|
170
169
|
return [{ type: "block-start", index: event.index, blockType: "tool-call" }];
|
|
171
170
|
}
|
|
172
171
|
return [];
|
|
@@ -184,7 +183,7 @@ function mapStreamEvent(event, toolCalls) {
|
|
|
184
183
|
return [{
|
|
185
184
|
type: "tool-call-delta",
|
|
186
185
|
index: event.index,
|
|
187
|
-
id: call?.callId ??
|
|
186
|
+
id: call?.callId ?? ToolCallId(`call-${event.index}`),
|
|
188
187
|
...call === void 0 ? {} : { name: call.name },
|
|
189
188
|
argumentsDelta: delta.partial_json
|
|
190
189
|
}];
|
|
@@ -552,7 +551,7 @@ var ClaudeCodeAgent = class {
|
|
|
552
551
|
this.dispatch.emit("agent/inbox/claimed", { message, turn });
|
|
553
552
|
}
|
|
554
553
|
});
|
|
555
|
-
const lastTurn = session.
|
|
554
|
+
const lastTurn = session.snapshotEvents().findLast((event) => event.type === "turn/start")?.data.turn ?? 0;
|
|
556
555
|
this.phase = { kind: "idle", lastTurn };
|
|
557
556
|
this.scope = createScope(loopCtx, this);
|
|
558
557
|
this.ctx = this.scope.ctx.extend({ agent: this });
|
|
@@ -756,7 +755,7 @@ var ClaudeCodeAgent = class {
|
|
|
756
755
|
*/
|
|
757
756
|
queryPermission() {
|
|
758
757
|
if (this.config.permissionMode !== void 0) return { permissionMode: this.config.permissionMode };
|
|
759
|
-
const permission = resolveSessionPermission(this.session.
|
|
758
|
+
const permission = resolveSessionPermission(this.session.snapshotEvents());
|
|
760
759
|
if (permission.kind === "bypass") return { permissionMode: "bypassPermissions" };
|
|
761
760
|
if (permission.kind === "ask") {
|
|
762
761
|
const approval = this.loopCtx.get("approval");
|
|
@@ -1162,7 +1161,7 @@ var ClaudeCodeLoop = class extends Service {
|
|
|
1162
1161
|
* fuses caller cancellation with lifecycle teardown for setup awaits.
|
|
1163
1162
|
*/
|
|
1164
1163
|
/* jscpd:ignore-start -- ownership/transaction machinery mirrors the default agent-loop factory; depending on agent-loop is forbidden. */
|
|
1165
|
-
prepare(ownerCtx, id, options, session, callerSignal) {
|
|
1164
|
+
prepare(ownerCtx, id, options, session, callerSignal, handle) {
|
|
1166
1165
|
ownerCtx.fiber.assertActive();
|
|
1167
1166
|
if (!this.ownership.isActive()) throw new Error("agent loop is not active");
|
|
1168
1167
|
if (callerSignal?.aborted) {
|
|
@@ -1196,11 +1195,15 @@ var ClaudeCodeLoop = class extends Service {
|
|
|
1196
1195
|
}
|
|
1197
1196
|
} finally {
|
|
1198
1197
|
try {
|
|
1199
|
-
|
|
1200
|
-
detachSession?.();
|
|
1198
|
+
await handle?.close();
|
|
1201
1199
|
} finally {
|
|
1202
|
-
|
|
1203
|
-
|
|
1200
|
+
try {
|
|
1201
|
+
detachAgent?.();
|
|
1202
|
+
detachSession?.();
|
|
1203
|
+
} finally {
|
|
1204
|
+
untrack();
|
|
1205
|
+
if (!ownerTriggered) await unfollowOwner();
|
|
1206
|
+
}
|
|
1204
1207
|
}
|
|
1205
1208
|
}
|
|
1206
1209
|
})();
|
|
@@ -1250,18 +1253,27 @@ var ClaudeCodeLoop = class extends Service {
|
|
|
1250
1253
|
}
|
|
1251
1254
|
}
|
|
1252
1255
|
/** Prepare one Agent around an acquired Session, run setup, and publish it. */
|
|
1253
|
-
async setupAndPublish(ownerCtx, id, preparation, agentOptions, setup, signal, source) {
|
|
1256
|
+
async setupAndPublish(ownerCtx, id, preparation, agentOptions, setup, signal, source, stored) {
|
|
1254
1257
|
var _stack = [];
|
|
1255
1258
|
try {
|
|
1256
1259
|
const ownedPreparation = __using(_stack, preparation);
|
|
1257
1260
|
const session = ownedPreparation.session;
|
|
1258
|
-
|
|
1261
|
+
let prepared;
|
|
1262
|
+
try {
|
|
1263
|
+
prepared = this.prepare(ownerCtx, id, agentOptions, session, signal, stored?.handle);
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
await stored?.handle.close().catch(() => {
|
|
1266
|
+
});
|
|
1267
|
+
throw error;
|
|
1268
|
+
}
|
|
1259
1269
|
try {
|
|
1260
1270
|
const setupCommit = await raceAbort(setup?.(prepared.agent.ctx), prepared.signal, id);
|
|
1261
1271
|
setupCommit?.commit();
|
|
1272
|
+
await this.appendUnstoredSuffix(stored, session);
|
|
1262
1273
|
return prepared.publish(source);
|
|
1263
1274
|
} catch (error) {
|
|
1264
|
-
await prepared.dispose()
|
|
1275
|
+
await prepared.dispose().catch(() => {
|
|
1276
|
+
});
|
|
1265
1277
|
throw error;
|
|
1266
1278
|
}
|
|
1267
1279
|
} catch (_) {
|
|
@@ -1272,7 +1284,8 @@ var ClaudeCodeLoop = class extends Service {
|
|
|
1272
1284
|
}
|
|
1273
1285
|
/**
|
|
1274
1286
|
* Create an agent and session under one caller-supplied identity, owned by
|
|
1275
|
-
* the accessing fiber.
|
|
1287
|
+
* the accessing fiber. When a persistence backend is mounted, the session's
|
|
1288
|
+
* durable identity is stored before publication.
|
|
1276
1289
|
* @param ownerCtx - caller context that structurally owns the lifecycle.
|
|
1277
1290
|
* @param options - identities, session seed/metadata, loop options, setup, and cancellation.
|
|
1278
1291
|
* @returns the published handle.
|
|
@@ -1280,20 +1293,73 @@ var ClaudeCodeLoop = class extends Service {
|
|
|
1280
1293
|
async createAgent(ownerCtx, options) {
|
|
1281
1294
|
const preparation = SessionPreparation.create(this.runtime.ctx.sessions.prepare(options.sessionId, {
|
|
1282
1295
|
...options.seed === void 0 ? {} : { seed: options.seed },
|
|
1283
|
-
...options.meta === void 0 ? {} : { meta: options.meta }
|
|
1296
|
+
...options.meta === void 0 ? {} : { meta: options.meta },
|
|
1297
|
+
...options.inheritedEventCount === void 0 ? {} : { inheritedEventCount: options.inheritedEventCount }
|
|
1284
1298
|
}));
|
|
1285
|
-
const published =
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1299
|
+
const published = (async () => {
|
|
1300
|
+
let stored;
|
|
1301
|
+
try {
|
|
1302
|
+
stored = options.signal === void 0 ? await this.createStoredSession(preparation.session) : await raceAbortCall(
|
|
1303
|
+
() => this.createStoredSession(preparation.session, options.signal),
|
|
1304
|
+
options.signal,
|
|
1305
|
+
options.sessionId,
|
|
1306
|
+
(abandoned) => {
|
|
1307
|
+
void abandoned?.handle.close().catch(() => {
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
);
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
preparation[Symbol.dispose]();
|
|
1313
|
+
throw error;
|
|
1314
|
+
}
|
|
1315
|
+
return this.setupAndPublish(
|
|
1316
|
+
ownerCtx,
|
|
1317
|
+
options.sessionId,
|
|
1318
|
+
preparation,
|
|
1319
|
+
options.agentOptions ?? {},
|
|
1320
|
+
options.setup,
|
|
1321
|
+
options.signal,
|
|
1322
|
+
"startup",
|
|
1323
|
+
stored
|
|
1324
|
+
);
|
|
1325
|
+
})();
|
|
1294
1326
|
this.ownership.trackWrapper(published);
|
|
1295
1327
|
return published;
|
|
1296
1328
|
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Take a fresh session's write ownership when persistence is mounted.
|
|
1331
|
+
* Nothing is appended here: the constructor seed (which never re-emits
|
|
1332
|
+
* through `session/event`) is stored by {@link appendUnstoredSuffix} at the
|
|
1333
|
+
* publication commit point, so a failed or cancelled setup closes an
|
|
1334
|
+
* unmaterialized handle and leaves no stored residue — the same id can be
|
|
1335
|
+
* created again.
|
|
1336
|
+
* @param session - the unpublished session to store.
|
|
1337
|
+
* @param signal - optional cancellation forwarded to the backend create.
|
|
1338
|
+
* @returns the owned handle and stored cursor, or `undefined` without a backend.
|
|
1339
|
+
*/
|
|
1340
|
+
async createStoredSession(session, signal) {
|
|
1341
|
+
const persistence = this.runtime.ctx.get("sessionPersistence");
|
|
1342
|
+
if (persistence === void 0) return void 0;
|
|
1343
|
+
const handle = await persistence.create(session.header, {
|
|
1344
|
+
inheritedEventCount: session.inheritedEventCount,
|
|
1345
|
+
...signal === void 0 ? {} : { signal }
|
|
1346
|
+
});
|
|
1347
|
+
return { handle, storedCount: 0 };
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Durably store the session events appended since the last stored cursor.
|
|
1351
|
+
* Pre-publication appends (constructor seed markers, setup-window events)
|
|
1352
|
+
* never re-emit through `session/event`, so publication must flush them
|
|
1353
|
+
* through the handle before live events start routing into it.
|
|
1354
|
+
* @param stored - the session's owned handle and stored cursor, if any.
|
|
1355
|
+
* @param session - the unpublished session whose suffix is stored.
|
|
1356
|
+
*/
|
|
1357
|
+
async appendUnstoredSuffix(stored, session) {
|
|
1358
|
+
if (stored === void 0) return;
|
|
1359
|
+
const suffix = session.snapshotEvents(SessionLogOffset(stored.storedCount));
|
|
1360
|
+
if (suffix.length > 0) await stored.handle.append(suffix);
|
|
1361
|
+
stored.storedCount += suffix.length;
|
|
1362
|
+
}
|
|
1297
1363
|
/**
|
|
1298
1364
|
* Resume an owned agent from the configured persistence service.
|
|
1299
1365
|
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
|
|
@@ -1307,11 +1373,10 @@ var ClaudeCodeLoop = class extends Service {
|
|
|
1307
1373
|
}
|
|
1308
1374
|
return this.resumeWith(ownerCtx, persistence, options);
|
|
1309
1375
|
}
|
|
1310
|
-
/** Resume through an explicit persistence
|
|
1311
|
-
|
|
1376
|
+
/** Resume through an explicit persistence service. */
|
|
1377
|
+
resumeWith(ownerCtx, persistence, options) {
|
|
1312
1378
|
const id = options.resumeSessionId;
|
|
1313
|
-
|
|
1314
|
-
try {
|
|
1379
|
+
const published = (async () => {
|
|
1315
1380
|
const ownerAbort = new AbortController();
|
|
1316
1381
|
const unfollowOwner = ownerCtx.effect(() => () => {
|
|
1317
1382
|
ownerAbort.abort(new Error(`agent "${id}" setup aborted: owner disposed during setup`));
|
|
@@ -1321,32 +1386,56 @@ var ClaudeCodeLoop = class extends Service {
|
|
|
1321
1386
|
ownerAbort.signal,
|
|
1322
1387
|
this.ownership.signal
|
|
1323
1388
|
]);
|
|
1389
|
+
let handle;
|
|
1390
|
+
let stored;
|
|
1391
|
+
let preparation;
|
|
1324
1392
|
try {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1393
|
+
try {
|
|
1394
|
+
handle = await raceAbortCall(
|
|
1395
|
+
() => persistence.open(id, "write", { signal: fused }),
|
|
1396
|
+
fused,
|
|
1397
|
+
id,
|
|
1398
|
+
(abandoned) => {
|
|
1399
|
+
void abandoned.close();
|
|
1400
|
+
}
|
|
1401
|
+
);
|
|
1402
|
+
const persisted = await handle.read(0, void 0, { signal: fused });
|
|
1403
|
+
fused.throwIfAborted();
|
|
1404
|
+
const closers = interruptedTurnClosers(persisted);
|
|
1405
|
+
if (closers.length > 0) await handle.append(closers);
|
|
1406
|
+
preparation = SessionPreparation.create(this.runtime.ctx.sessions.prepare(id, {
|
|
1407
|
+
seed: [...persisted, ...closers],
|
|
1408
|
+
meta: structuredClone(handle.header),
|
|
1409
|
+
inheritedEventCount: handle.inheritedEventCount,
|
|
1410
|
+
seedSource: "persistence"
|
|
1411
|
+
}));
|
|
1412
|
+
stored = { handle, storedCount: persisted.length + closers.length };
|
|
1413
|
+
await this.appendUnstoredSuffix(stored, preparation.session);
|
|
1414
|
+
} finally {
|
|
1415
|
+
await unfollowOwner();
|
|
1416
|
+
}
|
|
1417
|
+
ownerCtx.fiber.assertActive();
|
|
1418
|
+
if (!this.ownership.isActive()) throw new Error("agent loop is not active");
|
|
1419
|
+
const owned = stored;
|
|
1420
|
+
handle = void 0;
|
|
1421
|
+
return await this.setupAndPublish(
|
|
1422
|
+
ownerCtx,
|
|
1328
1423
|
id,
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1424
|
+
preparation,
|
|
1425
|
+
options.agentOptions ?? {},
|
|
1426
|
+
options.setup,
|
|
1427
|
+
options.signal,
|
|
1428
|
+
"resume",
|
|
1429
|
+
owned
|
|
1332
1430
|
);
|
|
1333
1431
|
} finally {
|
|
1334
|
-
|
|
1432
|
+
preparation?.[Symbol.dispose]();
|
|
1433
|
+
await handle?.close().catch(() => {
|
|
1434
|
+
});
|
|
1335
1435
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
ownerCtx,
|
|
1340
|
-
id,
|
|
1341
|
-
preparation,
|
|
1342
|
-
options.agentOptions ?? {},
|
|
1343
|
-
options.setup,
|
|
1344
|
-
options.signal,
|
|
1345
|
-
"resume"
|
|
1346
|
-
);
|
|
1347
|
-
} finally {
|
|
1348
|
-
preparation?.[Symbol.dispose]();
|
|
1349
|
-
}
|
|
1436
|
+
})();
|
|
1437
|
+
this.ownership.trackWrapper(published);
|
|
1438
|
+
return published;
|
|
1350
1439
|
}
|
|
1351
1440
|
};
|
|
1352
1441
|
|
|
@@ -1354,7 +1443,7 @@ var ClaudeCodeLoop = class extends Service {
|
|
|
1354
1443
|
import { Service as Service2 } from "@deepseek-ai/cordis";
|
|
1355
1444
|
import z2 from "@deepseek-ai/schemastery";
|
|
1356
1445
|
import { emitAgentEvent as emitAgentEvent2 } from "@deepseek-ai/dsh-agent";
|
|
1357
|
-
import { SessionPreparation as SessionPreparation2 } from "@deepseek-ai/dsh-session";
|
|
1446
|
+
import { interruptedTurnClosers as interruptedTurnClosers2, SessionLogOffset as SessionLogOffset2, SessionPreparation as SessionPreparation2 } from "@deepseek-ai/dsh-session";
|
|
1358
1447
|
|
|
1359
1448
|
// src/engine-codex/agent.ts
|
|
1360
1449
|
import { Inbox as Inbox2, agentEvents as agentEvents2 } from "@deepseek-ai/dsh-agent";
|
|
@@ -1441,7 +1530,7 @@ var AppServerClient = class _AppServerClient {
|
|
|
1441
1530
|
clientInfo: {
|
|
1442
1531
|
name: "dsh-loop-engine",
|
|
1443
1532
|
title: null,
|
|
1444
|
-
version: "0.
|
|
1533
|
+
version: "1.0.0-rc8"
|
|
1445
1534
|
},
|
|
1446
1535
|
capabilities: { experimentalApi: true, requestAttestation: false }
|
|
1447
1536
|
};
|
|
@@ -1656,7 +1745,7 @@ function noopNotificationHandler() {
|
|
|
1656
1745
|
}
|
|
1657
1746
|
|
|
1658
1747
|
// src/engine-codex/appserver/mapping.ts
|
|
1659
|
-
import {
|
|
1748
|
+
import { ToolCallId as ToolCallId2, createToolResultMessage as createToolResultMessage2 } from "@deepseek-ai/dsh-llm";
|
|
1660
1749
|
function mapUsage2(usage) {
|
|
1661
1750
|
return {
|
|
1662
1751
|
inputTokens: usage.inputTokens,
|
|
@@ -1668,12 +1757,12 @@ function mapUsage2(usage) {
|
|
|
1668
1757
|
function mapCommandExecution(item) {
|
|
1669
1758
|
return {
|
|
1670
1759
|
call: {
|
|
1671
|
-
callId:
|
|
1760
|
+
callId: ToolCallId2(item.id),
|
|
1672
1761
|
name: "command_execution",
|
|
1673
1762
|
arguments: JSON.stringify({ command: item.command ?? "" })
|
|
1674
1763
|
},
|
|
1675
1764
|
result: createToolResultMessage2({
|
|
1676
|
-
callId:
|
|
1765
|
+
callId: ToolCallId2(item.id),
|
|
1677
1766
|
content: [{ type: "text", text: item.aggregatedOutput ?? "" }],
|
|
1678
1767
|
isError: (item.exitCode ?? 0) !== 0 || item.status === "failed"
|
|
1679
1768
|
})
|
|
@@ -1682,12 +1771,12 @@ function mapCommandExecution(item) {
|
|
|
1682
1771
|
function mapFileChange(item) {
|
|
1683
1772
|
return {
|
|
1684
1773
|
call: {
|
|
1685
|
-
callId:
|
|
1774
|
+
callId: ToolCallId2(item.id),
|
|
1686
1775
|
name: "apply_patch",
|
|
1687
1776
|
arguments: JSON.stringify(item.changes ?? [])
|
|
1688
1777
|
},
|
|
1689
1778
|
result: createToolResultMessage2({
|
|
1690
|
-
callId:
|
|
1779
|
+
callId: ToolCallId2(item.id),
|
|
1691
1780
|
content: [{ type: "text", text: `patch ${item.status ?? "completed"}` }],
|
|
1692
1781
|
isError: item.status === "failed"
|
|
1693
1782
|
})
|
|
@@ -1698,12 +1787,12 @@ function mapMcpToolCall(item) {
|
|
|
1698
1787
|
const isError = item.error !== void 0 && item.error !== null;
|
|
1699
1788
|
return {
|
|
1700
1789
|
call: {
|
|
1701
|
-
callId:
|
|
1790
|
+
callId: ToolCallId2(item.id),
|
|
1702
1791
|
name: name2,
|
|
1703
1792
|
arguments: JSON.stringify(item.arguments ?? {})
|
|
1704
1793
|
},
|
|
1705
1794
|
result: createToolResultMessage2({
|
|
1706
|
-
callId:
|
|
1795
|
+
callId: ToolCallId2(item.id),
|
|
1707
1796
|
content: isError ? [{ type: "text", text: item.error?.message ?? "tool call failed" }] : [{ type: "text", text: JSON.stringify(item.result?.content ?? []) }],
|
|
1708
1797
|
isError
|
|
1709
1798
|
})
|
|
@@ -1732,7 +1821,7 @@ var CodexAgent = class {
|
|
|
1732
1821
|
this.dispatch.emit("agent/inbox/claimed", { message, turn });
|
|
1733
1822
|
}
|
|
1734
1823
|
});
|
|
1735
|
-
const lastTurn = session.
|
|
1824
|
+
const lastTurn = session.snapshotEvents().findLast((event) => event.type === "turn/start")?.data.turn ?? 0;
|
|
1736
1825
|
this.phase = { kind: "idle", lastTurn };
|
|
1737
1826
|
this.scope = createScope2(loopCtx, this);
|
|
1738
1827
|
this.ctx = this.scope.ctx.extend({ agent: this });
|
|
@@ -1945,7 +2034,7 @@ var CodexAgent = class {
|
|
|
1945
2034
|
* @returns the permission fields of the query spec.
|
|
1946
2035
|
*/
|
|
1947
2036
|
queryPermission() {
|
|
1948
|
-
const fold = resolveSessionPermission2(this.session.
|
|
2037
|
+
const fold = resolveSessionPermission2(this.session.snapshotEvents());
|
|
1949
2038
|
return {
|
|
1950
2039
|
sandboxMode: this.config.sandboxMode ?? fold.sandboxMode,
|
|
1951
2040
|
approvalPolicy: this.config.approvalPolicy ?? fold.approvalPolicy
|
|
@@ -2298,7 +2387,7 @@ var CodexLoop = class extends Service2 {
|
|
|
2298
2387
|
* fuses caller cancellation with lifecycle teardown for setup awaits.
|
|
2299
2388
|
*/
|
|
2300
2389
|
/* jscpd:ignore-start -- ownership/transaction machinery mirrors the Claude Code loop factory. */
|
|
2301
|
-
prepare(ownerCtx, id, options, session, callerSignal) {
|
|
2390
|
+
prepare(ownerCtx, id, options, session, callerSignal, handle) {
|
|
2302
2391
|
ownerCtx.fiber.assertActive();
|
|
2303
2392
|
if (!this.ownership.isActive()) throw new Error("agent loop is not active");
|
|
2304
2393
|
if (callerSignal?.aborted) {
|
|
@@ -2332,11 +2421,15 @@ var CodexLoop = class extends Service2 {
|
|
|
2332
2421
|
}
|
|
2333
2422
|
} finally {
|
|
2334
2423
|
try {
|
|
2335
|
-
|
|
2336
|
-
detachSession?.();
|
|
2424
|
+
await handle?.close();
|
|
2337
2425
|
} finally {
|
|
2338
|
-
|
|
2339
|
-
|
|
2426
|
+
try {
|
|
2427
|
+
detachAgent?.();
|
|
2428
|
+
detachSession?.();
|
|
2429
|
+
} finally {
|
|
2430
|
+
untrack();
|
|
2431
|
+
if (!ownerTriggered) await unfollowOwner();
|
|
2432
|
+
}
|
|
2340
2433
|
}
|
|
2341
2434
|
}
|
|
2342
2435
|
})();
|
|
@@ -2386,18 +2479,27 @@ var CodexLoop = class extends Service2 {
|
|
|
2386
2479
|
}
|
|
2387
2480
|
}
|
|
2388
2481
|
/** Prepare one Agent around an acquired Session, run setup, and publish it. */
|
|
2389
|
-
async setupAndPublish(ownerCtx, id, preparation, agentOptions, setup, signal, source) {
|
|
2482
|
+
async setupAndPublish(ownerCtx, id, preparation, agentOptions, setup, signal, source, stored) {
|
|
2390
2483
|
var _stack = [];
|
|
2391
2484
|
try {
|
|
2392
2485
|
const ownedPreparation = __using(_stack, preparation);
|
|
2393
2486
|
const session = ownedPreparation.session;
|
|
2394
|
-
|
|
2487
|
+
let prepared;
|
|
2488
|
+
try {
|
|
2489
|
+
prepared = this.prepare(ownerCtx, id, agentOptions, session, signal, stored?.handle);
|
|
2490
|
+
} catch (error) {
|
|
2491
|
+
await stored?.handle.close().catch(() => {
|
|
2492
|
+
});
|
|
2493
|
+
throw error;
|
|
2494
|
+
}
|
|
2395
2495
|
try {
|
|
2396
2496
|
const setupCommit = await raceAbort(setup?.(prepared.agent.ctx), prepared.signal, id);
|
|
2397
2497
|
setupCommit?.commit();
|
|
2498
|
+
await this.appendUnstoredSuffix(stored, session);
|
|
2398
2499
|
return prepared.publish(source);
|
|
2399
2500
|
} catch (error) {
|
|
2400
|
-
await prepared.dispose()
|
|
2501
|
+
await prepared.dispose().catch(() => {
|
|
2502
|
+
});
|
|
2401
2503
|
throw error;
|
|
2402
2504
|
}
|
|
2403
2505
|
} catch (_) {
|
|
@@ -2408,7 +2510,8 @@ var CodexLoop = class extends Service2 {
|
|
|
2408
2510
|
}
|
|
2409
2511
|
/**
|
|
2410
2512
|
* Create an agent and session under one caller-supplied identity, owned by
|
|
2411
|
-
* the accessing fiber.
|
|
2513
|
+
* the accessing fiber. When a persistence backend is mounted, the session's
|
|
2514
|
+
* durable identity is stored before publication.
|
|
2412
2515
|
* @param ownerCtx - caller context that structurally owns the lifecycle.
|
|
2413
2516
|
* @param options - identities, session seed/metadata, loop options, setup, and cancellation.
|
|
2414
2517
|
* @returns the published handle.
|
|
@@ -2416,20 +2519,73 @@ var CodexLoop = class extends Service2 {
|
|
|
2416
2519
|
async createAgent(ownerCtx, options) {
|
|
2417
2520
|
const preparation = SessionPreparation2.create(this.runtime.ctx.sessions.prepare(options.sessionId, {
|
|
2418
2521
|
...options.seed === void 0 ? {} : { seed: options.seed },
|
|
2419
|
-
...options.meta === void 0 ? {} : { meta: options.meta }
|
|
2522
|
+
...options.meta === void 0 ? {} : { meta: options.meta },
|
|
2523
|
+
...options.inheritedEventCount === void 0 ? {} : { inheritedEventCount: options.inheritedEventCount }
|
|
2420
2524
|
}));
|
|
2421
|
-
const published =
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2525
|
+
const published = (async () => {
|
|
2526
|
+
let stored;
|
|
2527
|
+
try {
|
|
2528
|
+
stored = options.signal === void 0 ? await this.createStoredSession(preparation.session) : await raceAbortCall(
|
|
2529
|
+
() => this.createStoredSession(preparation.session, options.signal),
|
|
2530
|
+
options.signal,
|
|
2531
|
+
options.sessionId,
|
|
2532
|
+
(abandoned) => {
|
|
2533
|
+
void abandoned?.handle.close().catch(() => {
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
);
|
|
2537
|
+
} catch (error) {
|
|
2538
|
+
preparation[Symbol.dispose]();
|
|
2539
|
+
throw error;
|
|
2540
|
+
}
|
|
2541
|
+
return this.setupAndPublish(
|
|
2542
|
+
ownerCtx,
|
|
2543
|
+
options.sessionId,
|
|
2544
|
+
preparation,
|
|
2545
|
+
options.agentOptions ?? {},
|
|
2546
|
+
options.setup,
|
|
2547
|
+
options.signal,
|
|
2548
|
+
"startup",
|
|
2549
|
+
stored
|
|
2550
|
+
);
|
|
2551
|
+
})();
|
|
2430
2552
|
this.ownership.trackWrapper(published);
|
|
2431
2553
|
return published;
|
|
2432
2554
|
}
|
|
2555
|
+
/**
|
|
2556
|
+
* Take a fresh session's write ownership when persistence is mounted.
|
|
2557
|
+
* Nothing is appended here: the constructor seed (which never re-emits
|
|
2558
|
+
* through `session/event`) is stored by {@link appendUnstoredSuffix} at the
|
|
2559
|
+
* publication commit point, so a failed or cancelled setup closes an
|
|
2560
|
+
* unmaterialized handle and leaves no stored residue — the same id can be
|
|
2561
|
+
* created again.
|
|
2562
|
+
* @param session - the unpublished session to store.
|
|
2563
|
+
* @param signal - optional cancellation forwarded to the backend create.
|
|
2564
|
+
* @returns the owned handle and stored cursor, or `undefined` without a backend.
|
|
2565
|
+
*/
|
|
2566
|
+
async createStoredSession(session, signal) {
|
|
2567
|
+
const persistence = this.runtime.ctx.get("sessionPersistence");
|
|
2568
|
+
if (persistence === void 0) return void 0;
|
|
2569
|
+
const handle = await persistence.create(session.header, {
|
|
2570
|
+
inheritedEventCount: session.inheritedEventCount,
|
|
2571
|
+
...signal === void 0 ? {} : { signal }
|
|
2572
|
+
});
|
|
2573
|
+
return { handle, storedCount: 0 };
|
|
2574
|
+
}
|
|
2575
|
+
/**
|
|
2576
|
+
* Durably store the session events appended since the last stored cursor.
|
|
2577
|
+
* Pre-publication appends (constructor seed markers, setup-window events)
|
|
2578
|
+
* never re-emit through `session/event`, so publication must flush them
|
|
2579
|
+
* through the handle before live events start routing into it.
|
|
2580
|
+
* @param stored - the session's owned handle and stored cursor, if any.
|
|
2581
|
+
* @param session - the unpublished session whose suffix is stored.
|
|
2582
|
+
*/
|
|
2583
|
+
async appendUnstoredSuffix(stored, session) {
|
|
2584
|
+
if (stored === void 0) return;
|
|
2585
|
+
const suffix = session.snapshotEvents(SessionLogOffset2(stored.storedCount));
|
|
2586
|
+
if (suffix.length > 0) await stored.handle.append(suffix);
|
|
2587
|
+
stored.storedCount += suffix.length;
|
|
2588
|
+
}
|
|
2433
2589
|
/**
|
|
2434
2590
|
* Resume an owned agent from the configured persistence service.
|
|
2435
2591
|
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
|
|
@@ -2443,11 +2599,10 @@ var CodexLoop = class extends Service2 {
|
|
|
2443
2599
|
}
|
|
2444
2600
|
return this.resumeWith(ownerCtx, persistence, options);
|
|
2445
2601
|
}
|
|
2446
|
-
/** Resume through an explicit persistence
|
|
2447
|
-
|
|
2602
|
+
/** Resume through an explicit persistence service. */
|
|
2603
|
+
resumeWith(ownerCtx, persistence, options) {
|
|
2448
2604
|
const id = options.resumeSessionId;
|
|
2449
|
-
|
|
2450
|
-
try {
|
|
2605
|
+
const published = (async () => {
|
|
2451
2606
|
const ownerAbort = new AbortController();
|
|
2452
2607
|
const unfollowOwner = ownerCtx.effect(() => () => {
|
|
2453
2608
|
ownerAbort.abort(new Error(`agent "${id}" setup aborted: owner disposed during setup`));
|
|
@@ -2457,32 +2612,56 @@ var CodexLoop = class extends Service2 {
|
|
|
2457
2612
|
ownerAbort.signal,
|
|
2458
2613
|
this.ownership.signal
|
|
2459
2614
|
]);
|
|
2615
|
+
let handle;
|
|
2616
|
+
let stored;
|
|
2617
|
+
let preparation;
|
|
2460
2618
|
try {
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2619
|
+
try {
|
|
2620
|
+
handle = await raceAbortCall(
|
|
2621
|
+
() => persistence.open(id, "write", { signal: fused }),
|
|
2622
|
+
fused,
|
|
2623
|
+
id,
|
|
2624
|
+
(abandoned) => {
|
|
2625
|
+
void abandoned.close();
|
|
2626
|
+
}
|
|
2627
|
+
);
|
|
2628
|
+
const persisted = await handle.read(0, void 0, { signal: fused });
|
|
2629
|
+
fused.throwIfAborted();
|
|
2630
|
+
const closers = interruptedTurnClosers2(persisted);
|
|
2631
|
+
if (closers.length > 0) await handle.append(closers);
|
|
2632
|
+
preparation = SessionPreparation2.create(this.runtime.ctx.sessions.prepare(id, {
|
|
2633
|
+
seed: [...persisted, ...closers],
|
|
2634
|
+
meta: structuredClone(handle.header),
|
|
2635
|
+
inheritedEventCount: handle.inheritedEventCount,
|
|
2636
|
+
seedSource: "persistence"
|
|
2637
|
+
}));
|
|
2638
|
+
stored = { handle, storedCount: persisted.length + closers.length };
|
|
2639
|
+
await this.appendUnstoredSuffix(stored, preparation.session);
|
|
2640
|
+
} finally {
|
|
2641
|
+
await unfollowOwner();
|
|
2642
|
+
}
|
|
2643
|
+
ownerCtx.fiber.assertActive();
|
|
2644
|
+
if (!this.ownership.isActive()) throw new Error("agent loop is not active");
|
|
2645
|
+
const owned = stored;
|
|
2646
|
+
handle = void 0;
|
|
2647
|
+
return await this.setupAndPublish(
|
|
2648
|
+
ownerCtx,
|
|
2464
2649
|
id,
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2650
|
+
preparation,
|
|
2651
|
+
options.agentOptions ?? {},
|
|
2652
|
+
options.setup,
|
|
2653
|
+
options.signal,
|
|
2654
|
+
"resume",
|
|
2655
|
+
owned
|
|
2468
2656
|
);
|
|
2469
2657
|
} finally {
|
|
2470
|
-
|
|
2658
|
+
preparation?.[Symbol.dispose]();
|
|
2659
|
+
await handle?.close().catch(() => {
|
|
2660
|
+
});
|
|
2471
2661
|
}
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
ownerCtx,
|
|
2476
|
-
id,
|
|
2477
|
-
preparation,
|
|
2478
|
-
options.agentOptions ?? {},
|
|
2479
|
-
options.setup,
|
|
2480
|
-
options.signal,
|
|
2481
|
-
"resume"
|
|
2482
|
-
);
|
|
2483
|
-
} finally {
|
|
2484
|
-
preparation?.[Symbol.dispose]();
|
|
2485
|
-
}
|
|
2662
|
+
})();
|
|
2663
|
+
this.ownership.trackWrapper(published);
|
|
2664
|
+
return published;
|
|
2486
2665
|
}
|
|
2487
2666
|
};
|
|
2488
2667
|
|
|
@@ -2493,11 +2672,11 @@ import { fileURLToPath } from "node:url";
|
|
|
2493
2672
|
import { Service as Service3 } from "@deepseek-ai/cordis";
|
|
2494
2673
|
import z3 from "@deepseek-ai/schemastery";
|
|
2495
2674
|
import { emitAgentEvent as emitAgentEvent3 } from "@deepseek-ai/dsh-agent";
|
|
2496
|
-
import { SessionPreparation as SessionPreparation3 } from "@deepseek-ai/dsh-session";
|
|
2675
|
+
import { interruptedTurnClosers as interruptedTurnClosers3, SessionLogOffset as SessionLogOffset3, SessionPreparation as SessionPreparation3 } from "@deepseek-ai/dsh-session";
|
|
2497
2676
|
|
|
2498
2677
|
// src/engine-pi/agent.ts
|
|
2499
2678
|
import { Inbox as Inbox3, agentEvents as agentEvents3 } from "@deepseek-ai/dsh-agent";
|
|
2500
|
-
import {
|
|
2679
|
+
import { ToolCallId as ToolCallId4, LlmError as LlmError3, createAssistantMessage as createAssistantMessage3, createUserMessage as createUserMessage3, errorChain as errorChain3 } from "@deepseek-ai/dsh-llm";
|
|
2501
2680
|
import { createScope as createScope3 } from "@deepseek-ai/dsh-scope";
|
|
2502
2681
|
import { canonicalHeader as canonicalHeader3 } from "@deepseek-ai/dsh-session";
|
|
2503
2682
|
|
|
@@ -2717,7 +2896,7 @@ var PiRpcClient = class _PiRpcClient {
|
|
|
2717
2896
|
|
|
2718
2897
|
// src/engine-pi/rpc/mapping.ts
|
|
2719
2898
|
import {
|
|
2720
|
-
|
|
2899
|
+
ToolCallId as ToolCallId3,
|
|
2721
2900
|
createToolResultMessage as createToolResultMessage3
|
|
2722
2901
|
} from "@deepseek-ai/dsh-llm";
|
|
2723
2902
|
function mapUsage3(usage) {
|
|
@@ -2748,7 +2927,7 @@ function resultText(content) {
|
|
|
2748
2927
|
}
|
|
2749
2928
|
function mapToolResult(ev) {
|
|
2750
2929
|
return createToolResultMessage3({
|
|
2751
|
-
callId:
|
|
2930
|
+
callId: ToolCallId3(ev.toolCallId),
|
|
2752
2931
|
content: [{ type: "text", text: resultText(ev.result) || "(no content)" }],
|
|
2753
2932
|
isError: ev.isError
|
|
2754
2933
|
});
|
|
@@ -2783,7 +2962,7 @@ var PiAgent = class {
|
|
|
2783
2962
|
this.dispatch.emit("agent/inbox/claimed", { message, turn });
|
|
2784
2963
|
}
|
|
2785
2964
|
});
|
|
2786
|
-
const lastTurn = session.
|
|
2965
|
+
const lastTurn = session.snapshotEvents().findLast((event) => event.type === "turn/start")?.data.turn ?? 0;
|
|
2787
2966
|
this.phase = { kind: "idle", lastTurn };
|
|
2788
2967
|
this.scope = createScope3(loopCtx, this);
|
|
2789
2968
|
this.ctx = this.scope.ctx.extend({ agent: this });
|
|
@@ -3004,7 +3183,7 @@ var PiAgent = class {
|
|
|
3004
3183
|
* @returns the permission fields of the query spec.
|
|
3005
3184
|
*/
|
|
3006
3185
|
queryPermission() {
|
|
3007
|
-
const fold = resolveSessionPermission3(this.session.
|
|
3186
|
+
const fold = resolveSessionPermission3(this.session.snapshotEvents());
|
|
3008
3187
|
const sandboxMode = this.config.sandboxMode ?? fold.sandboxMode;
|
|
3009
3188
|
return {
|
|
3010
3189
|
sandboxMode,
|
|
@@ -3215,7 +3394,7 @@ var PiAgent = class {
|
|
|
3215
3394
|
this.session.append("tool/call", {
|
|
3216
3395
|
turn,
|
|
3217
3396
|
step,
|
|
3218
|
-
callId:
|
|
3397
|
+
callId: ToolCallId4(callId),
|
|
3219
3398
|
name: name2,
|
|
3220
3399
|
arguments: argumentsValue
|
|
3221
3400
|
});
|
|
@@ -3452,7 +3631,7 @@ var PiLoop = class extends Service3 {
|
|
|
3452
3631
|
* fuses caller cancellation with lifecycle teardown for setup awaits.
|
|
3453
3632
|
*/
|
|
3454
3633
|
/* jscpd:ignore-start -- ownership/transaction machinery mirrors the Claude Code loop factory. */
|
|
3455
|
-
prepare(ownerCtx, id, options, session, callerSignal) {
|
|
3634
|
+
prepare(ownerCtx, id, options, session, callerSignal, handle) {
|
|
3456
3635
|
ownerCtx.fiber.assertActive();
|
|
3457
3636
|
if (!this.ownership.isActive()) throw new Error("agent loop is not active");
|
|
3458
3637
|
if (callerSignal?.aborted) {
|
|
@@ -3486,11 +3665,15 @@ var PiLoop = class extends Service3 {
|
|
|
3486
3665
|
}
|
|
3487
3666
|
} finally {
|
|
3488
3667
|
try {
|
|
3489
|
-
|
|
3490
|
-
detachSession?.();
|
|
3668
|
+
await handle?.close();
|
|
3491
3669
|
} finally {
|
|
3492
|
-
|
|
3493
|
-
|
|
3670
|
+
try {
|
|
3671
|
+
detachAgent?.();
|
|
3672
|
+
detachSession?.();
|
|
3673
|
+
} finally {
|
|
3674
|
+
untrack();
|
|
3675
|
+
if (!ownerTriggered) await unfollowOwner();
|
|
3676
|
+
}
|
|
3494
3677
|
}
|
|
3495
3678
|
}
|
|
3496
3679
|
})();
|
|
@@ -3540,18 +3723,27 @@ var PiLoop = class extends Service3 {
|
|
|
3540
3723
|
}
|
|
3541
3724
|
}
|
|
3542
3725
|
/** Prepare one Agent around an acquired Session, run setup, and publish it. */
|
|
3543
|
-
async setupAndPublish(ownerCtx, id, preparation, agentOptions, setup, signal, source) {
|
|
3726
|
+
async setupAndPublish(ownerCtx, id, preparation, agentOptions, setup, signal, source, stored) {
|
|
3544
3727
|
var _stack = [];
|
|
3545
3728
|
try {
|
|
3546
3729
|
const ownedPreparation = __using(_stack, preparation);
|
|
3547
3730
|
const session = ownedPreparation.session;
|
|
3548
|
-
|
|
3731
|
+
let prepared;
|
|
3732
|
+
try {
|
|
3733
|
+
prepared = this.prepare(ownerCtx, id, agentOptions, session, signal, stored?.handle);
|
|
3734
|
+
} catch (error) {
|
|
3735
|
+
await stored?.handle.close().catch(() => {
|
|
3736
|
+
});
|
|
3737
|
+
throw error;
|
|
3738
|
+
}
|
|
3549
3739
|
try {
|
|
3550
3740
|
const setupCommit = await raceAbort(setup?.(prepared.agent.ctx), prepared.signal, id);
|
|
3551
3741
|
setupCommit?.commit();
|
|
3742
|
+
await this.appendUnstoredSuffix(stored, session);
|
|
3552
3743
|
return prepared.publish(source);
|
|
3553
3744
|
} catch (error) {
|
|
3554
|
-
await prepared.dispose()
|
|
3745
|
+
await prepared.dispose().catch(() => {
|
|
3746
|
+
});
|
|
3555
3747
|
throw error;
|
|
3556
3748
|
}
|
|
3557
3749
|
} catch (_) {
|
|
@@ -3562,7 +3754,8 @@ var PiLoop = class extends Service3 {
|
|
|
3562
3754
|
}
|
|
3563
3755
|
/**
|
|
3564
3756
|
* Create an agent and session under one caller-supplied identity, owned by
|
|
3565
|
-
* the accessing fiber.
|
|
3757
|
+
* the accessing fiber. When a persistence backend is mounted, the session's
|
|
3758
|
+
* durable identity is stored before publication.
|
|
3566
3759
|
* @param ownerCtx - caller context that structurally owns the lifecycle.
|
|
3567
3760
|
* @param options - identities, session seed/metadata, loop options, setup, and cancellation.
|
|
3568
3761
|
* @returns the published handle.
|
|
@@ -3570,20 +3763,73 @@ var PiLoop = class extends Service3 {
|
|
|
3570
3763
|
async createAgent(ownerCtx, options) {
|
|
3571
3764
|
const preparation = SessionPreparation3.create(this.runtime.ctx.sessions.prepare(options.sessionId, {
|
|
3572
3765
|
...options.seed === void 0 ? {} : { seed: options.seed },
|
|
3573
|
-
...options.meta === void 0 ? {} : { meta: options.meta }
|
|
3766
|
+
...options.meta === void 0 ? {} : { meta: options.meta },
|
|
3767
|
+
...options.inheritedEventCount === void 0 ? {} : { inheritedEventCount: options.inheritedEventCount }
|
|
3574
3768
|
}));
|
|
3575
|
-
const published =
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3769
|
+
const published = (async () => {
|
|
3770
|
+
let stored;
|
|
3771
|
+
try {
|
|
3772
|
+
stored = options.signal === void 0 ? await this.createStoredSession(preparation.session) : await raceAbortCall(
|
|
3773
|
+
() => this.createStoredSession(preparation.session, options.signal),
|
|
3774
|
+
options.signal,
|
|
3775
|
+
options.sessionId,
|
|
3776
|
+
(abandoned) => {
|
|
3777
|
+
void abandoned?.handle.close().catch(() => {
|
|
3778
|
+
});
|
|
3779
|
+
}
|
|
3780
|
+
);
|
|
3781
|
+
} catch (error) {
|
|
3782
|
+
preparation[Symbol.dispose]();
|
|
3783
|
+
throw error;
|
|
3784
|
+
}
|
|
3785
|
+
return this.setupAndPublish(
|
|
3786
|
+
ownerCtx,
|
|
3787
|
+
options.sessionId,
|
|
3788
|
+
preparation,
|
|
3789
|
+
options.agentOptions ?? {},
|
|
3790
|
+
options.setup,
|
|
3791
|
+
options.signal,
|
|
3792
|
+
"startup",
|
|
3793
|
+
stored
|
|
3794
|
+
);
|
|
3795
|
+
})();
|
|
3584
3796
|
this.ownership.trackWrapper(published);
|
|
3585
3797
|
return published;
|
|
3586
3798
|
}
|
|
3799
|
+
/**
|
|
3800
|
+
* Take a fresh session's write ownership when persistence is mounted.
|
|
3801
|
+
* Nothing is appended here: the constructor seed (which never re-emits
|
|
3802
|
+
* through `session/event`) is stored by {@link appendUnstoredSuffix} at the
|
|
3803
|
+
* publication commit point, so a failed or cancelled setup closes an
|
|
3804
|
+
* unmaterialized handle and leaves no stored residue — the same id can be
|
|
3805
|
+
* created again.
|
|
3806
|
+
* @param session - the unpublished session to store.
|
|
3807
|
+
* @param signal - optional cancellation forwarded to the backend create.
|
|
3808
|
+
* @returns the owned handle and stored cursor, or `undefined` without a backend.
|
|
3809
|
+
*/
|
|
3810
|
+
async createStoredSession(session, signal) {
|
|
3811
|
+
const persistence = this.runtime.ctx.get("sessionPersistence");
|
|
3812
|
+
if (persistence === void 0) return void 0;
|
|
3813
|
+
const handle = await persistence.create(session.header, {
|
|
3814
|
+
inheritedEventCount: session.inheritedEventCount,
|
|
3815
|
+
...signal === void 0 ? {} : { signal }
|
|
3816
|
+
});
|
|
3817
|
+
return { handle, storedCount: 0 };
|
|
3818
|
+
}
|
|
3819
|
+
/**
|
|
3820
|
+
* Durably store the session events appended since the last stored cursor.
|
|
3821
|
+
* Pre-publication appends (constructor seed markers, setup-window events)
|
|
3822
|
+
* never re-emit through `session/event`, so publication must flush them
|
|
3823
|
+
* through the handle before live events start routing into it.
|
|
3824
|
+
* @param stored - the session's owned handle and stored cursor, if any.
|
|
3825
|
+
* @param session - the unpublished session whose suffix is stored.
|
|
3826
|
+
*/
|
|
3827
|
+
async appendUnstoredSuffix(stored, session) {
|
|
3828
|
+
if (stored === void 0) return;
|
|
3829
|
+
const suffix = session.snapshotEvents(SessionLogOffset3(stored.storedCount));
|
|
3830
|
+
if (suffix.length > 0) await stored.handle.append(suffix);
|
|
3831
|
+
stored.storedCount += suffix.length;
|
|
3832
|
+
}
|
|
3587
3833
|
/**
|
|
3588
3834
|
* Resume an owned agent from the configured persistence service.
|
|
3589
3835
|
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
|
|
@@ -3597,11 +3843,10 @@ var PiLoop = class extends Service3 {
|
|
|
3597
3843
|
}
|
|
3598
3844
|
return this.resumeWith(ownerCtx, persistence, options);
|
|
3599
3845
|
}
|
|
3600
|
-
/** Resume through an explicit persistence
|
|
3601
|
-
|
|
3846
|
+
/** Resume through an explicit persistence service. */
|
|
3847
|
+
resumeWith(ownerCtx, persistence, options) {
|
|
3602
3848
|
const id = options.resumeSessionId;
|
|
3603
|
-
|
|
3604
|
-
try {
|
|
3849
|
+
const published = (async () => {
|
|
3605
3850
|
const ownerAbort = new AbortController();
|
|
3606
3851
|
const unfollowOwner = ownerCtx.effect(() => () => {
|
|
3607
3852
|
ownerAbort.abort(new Error(`agent "${id}" setup aborted: owner disposed during setup`));
|
|
@@ -3611,32 +3856,56 @@ var PiLoop = class extends Service3 {
|
|
|
3611
3856
|
ownerAbort.signal,
|
|
3612
3857
|
this.ownership.signal
|
|
3613
3858
|
]);
|
|
3859
|
+
let handle;
|
|
3860
|
+
let stored;
|
|
3861
|
+
let preparation;
|
|
3614
3862
|
try {
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3863
|
+
try {
|
|
3864
|
+
handle = await raceAbortCall(
|
|
3865
|
+
() => persistence.open(id, "write", { signal: fused }),
|
|
3866
|
+
fused,
|
|
3867
|
+
id,
|
|
3868
|
+
(abandoned) => {
|
|
3869
|
+
void abandoned.close();
|
|
3870
|
+
}
|
|
3871
|
+
);
|
|
3872
|
+
const persisted = await handle.read(0, void 0, { signal: fused });
|
|
3873
|
+
fused.throwIfAborted();
|
|
3874
|
+
const closers = interruptedTurnClosers3(persisted);
|
|
3875
|
+
if (closers.length > 0) await handle.append(closers);
|
|
3876
|
+
preparation = SessionPreparation3.create(this.runtime.ctx.sessions.prepare(id, {
|
|
3877
|
+
seed: [...persisted, ...closers],
|
|
3878
|
+
meta: structuredClone(handle.header),
|
|
3879
|
+
inheritedEventCount: handle.inheritedEventCount,
|
|
3880
|
+
seedSource: "persistence"
|
|
3881
|
+
}));
|
|
3882
|
+
stored = { handle, storedCount: persisted.length + closers.length };
|
|
3883
|
+
await this.appendUnstoredSuffix(stored, preparation.session);
|
|
3884
|
+
} finally {
|
|
3885
|
+
await unfollowOwner();
|
|
3886
|
+
}
|
|
3887
|
+
ownerCtx.fiber.assertActive();
|
|
3888
|
+
if (!this.ownership.isActive()) throw new Error("agent loop is not active");
|
|
3889
|
+
const owned = stored;
|
|
3890
|
+
handle = void 0;
|
|
3891
|
+
return await this.setupAndPublish(
|
|
3892
|
+
ownerCtx,
|
|
3618
3893
|
id,
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3894
|
+
preparation,
|
|
3895
|
+
options.agentOptions ?? {},
|
|
3896
|
+
options.setup,
|
|
3897
|
+
options.signal,
|
|
3898
|
+
"resume",
|
|
3899
|
+
owned
|
|
3622
3900
|
);
|
|
3623
3901
|
} finally {
|
|
3624
|
-
|
|
3902
|
+
preparation?.[Symbol.dispose]();
|
|
3903
|
+
await handle?.close().catch(() => {
|
|
3904
|
+
});
|
|
3625
3905
|
}
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
ownerCtx,
|
|
3630
|
-
id,
|
|
3631
|
-
preparation,
|
|
3632
|
-
options.agentOptions ?? {},
|
|
3633
|
-
options.setup,
|
|
3634
|
-
options.signal,
|
|
3635
|
-
"resume"
|
|
3636
|
-
);
|
|
3637
|
-
} finally {
|
|
3638
|
-
preparation?.[Symbol.dispose]();
|
|
3639
|
-
}
|
|
3906
|
+
})();
|
|
3907
|
+
this.ownership.trackWrapper(published);
|
|
3908
|
+
return published;
|
|
3640
3909
|
}
|
|
3641
3910
|
};
|
|
3642
3911
|
|
|
@@ -3644,11 +3913,11 @@ var PiLoop = class extends Service3 {
|
|
|
3644
3913
|
import { Service as Service4 } from "@deepseek-ai/cordis";
|
|
3645
3914
|
import z4 from "@deepseek-ai/schemastery";
|
|
3646
3915
|
import { emitAgentEvent as emitAgentEvent4 } from "@deepseek-ai/dsh-agent";
|
|
3647
|
-
import { SessionPreparation as SessionPreparation4 } from "@deepseek-ai/dsh-session";
|
|
3916
|
+
import { interruptedTurnClosers as interruptedTurnClosers4, SessionLogOffset as SessionLogOffset4, SessionPreparation as SessionPreparation4 } from "@deepseek-ai/dsh-session";
|
|
3648
3917
|
|
|
3649
3918
|
// src/engine-kimi/agent.ts
|
|
3650
3919
|
import { Inbox as Inbox4, agentEvents as agentEvents4 } from "@deepseek-ai/dsh-agent";
|
|
3651
|
-
import {
|
|
3920
|
+
import { ToolCallId as ToolCallId6, LlmError as LlmError4, createAssistantMessage as createAssistantMessage4, createUserMessage as createUserMessage4, errorChain as errorChain4 } from "@deepseek-ai/dsh-llm";
|
|
3652
3921
|
import { createScope as createScope4 } from "@deepseek-ai/dsh-scope";
|
|
3653
3922
|
import { canonicalHeader as canonicalHeader4 } from "@deepseek-ai/dsh-session";
|
|
3654
3923
|
|
|
@@ -3903,7 +4172,7 @@ var AcpClient = class _AcpClient {
|
|
|
3903
4172
|
};
|
|
3904
4173
|
|
|
3905
4174
|
// src/engine-kimi/acp/mapping.ts
|
|
3906
|
-
import {
|
|
4175
|
+
import { ToolCallId as ToolCallId5, createToolResultMessage as createToolResultMessage4 } from "@deepseek-ai/dsh-llm";
|
|
3907
4176
|
function isTextChunk(update) {
|
|
3908
4177
|
return update.sessionUpdate === "agent_message_chunk";
|
|
3909
4178
|
}
|
|
@@ -3940,7 +4209,7 @@ function toolContentText(update) {
|
|
|
3940
4209
|
}
|
|
3941
4210
|
function toolResult(callId, text, isError) {
|
|
3942
4211
|
return createToolResultMessage4({
|
|
3943
|
-
callId:
|
|
4212
|
+
callId: ToolCallId5(callId),
|
|
3944
4213
|
content: [{ type: "text", text: text.length > 0 ? text : "(no content)" }],
|
|
3945
4214
|
isError
|
|
3946
4215
|
});
|
|
@@ -3975,7 +4244,7 @@ var KimiAgent = class {
|
|
|
3975
4244
|
this.dispatch.emit("agent/inbox/claimed", { message, turn });
|
|
3976
4245
|
}
|
|
3977
4246
|
});
|
|
3978
|
-
const lastTurn = session.
|
|
4247
|
+
const lastTurn = session.snapshotEvents().findLast((event) => event.type === "turn/start")?.data.turn ?? 0;
|
|
3979
4248
|
this.phase = { kind: "idle", lastTurn };
|
|
3980
4249
|
this.scope = createScope4(loopCtx, this);
|
|
3981
4250
|
this.ctx = this.scope.ctx.extend({ agent: this });
|
|
@@ -4321,7 +4590,7 @@ var KimiAgent = class {
|
|
|
4321
4590
|
signal.throwIfAborted();
|
|
4322
4591
|
const client = await this.acpClient(cwd);
|
|
4323
4592
|
signal.throwIfAborted();
|
|
4324
|
-
client.onPermission(() => resolveToolApproval(this.session.
|
|
4593
|
+
client.onPermission(() => resolveToolApproval(this.session.snapshotEvents()));
|
|
4325
4594
|
const acpSessionId = await client.newSession(cwd);
|
|
4326
4595
|
signal.throwIfAborted();
|
|
4327
4596
|
client.onUpdate((update) => this.applyUpdate(turn, step, update));
|
|
@@ -4385,7 +4654,7 @@ var KimiAgent = class {
|
|
|
4385
4654
|
if (callId === "" || this.emittedToolCalls.has(callId)) return;
|
|
4386
4655
|
this.emittedToolCalls.add(callId);
|
|
4387
4656
|
const name2 = toolCallName(update);
|
|
4388
|
-
this.session.append("tool/call", { turn, step, callId:
|
|
4657
|
+
this.session.append("tool/call", { turn, step, callId: ToolCallId6(callId), name: name2, arguments: "{}" });
|
|
4389
4658
|
this.toolText.set(callId, "");
|
|
4390
4659
|
return;
|
|
4391
4660
|
}
|
|
@@ -4476,7 +4745,7 @@ var KimiLoop = class extends Service4 {
|
|
|
4476
4745
|
* fuses caller cancellation with lifecycle teardown for setup awaits.
|
|
4477
4746
|
*/
|
|
4478
4747
|
/* jscpd:ignore-start -- ownership/transaction machinery mirrors the Claude Code loop factory. */
|
|
4479
|
-
prepare(ownerCtx, id, options, session, callerSignal) {
|
|
4748
|
+
prepare(ownerCtx, id, options, session, callerSignal, handle) {
|
|
4480
4749
|
ownerCtx.fiber.assertActive();
|
|
4481
4750
|
if (!this.ownership.isActive()) throw new Error("agent loop is not active");
|
|
4482
4751
|
if (callerSignal?.aborted) {
|
|
@@ -4510,11 +4779,15 @@ var KimiLoop = class extends Service4 {
|
|
|
4510
4779
|
}
|
|
4511
4780
|
} finally {
|
|
4512
4781
|
try {
|
|
4513
|
-
|
|
4514
|
-
detachSession?.();
|
|
4782
|
+
await handle?.close();
|
|
4515
4783
|
} finally {
|
|
4516
|
-
|
|
4517
|
-
|
|
4784
|
+
try {
|
|
4785
|
+
detachAgent?.();
|
|
4786
|
+
detachSession?.();
|
|
4787
|
+
} finally {
|
|
4788
|
+
untrack();
|
|
4789
|
+
if (!ownerTriggered) await unfollowOwner();
|
|
4790
|
+
}
|
|
4518
4791
|
}
|
|
4519
4792
|
}
|
|
4520
4793
|
})();
|
|
@@ -4564,18 +4837,27 @@ var KimiLoop = class extends Service4 {
|
|
|
4564
4837
|
}
|
|
4565
4838
|
}
|
|
4566
4839
|
/** Prepare one Agent around an acquired Session, run setup, and publish it. */
|
|
4567
|
-
async setupAndPublish(ownerCtx, id, preparation, agentOptions, setup, signal, source) {
|
|
4840
|
+
async setupAndPublish(ownerCtx, id, preparation, agentOptions, setup, signal, source, stored) {
|
|
4568
4841
|
var _stack = [];
|
|
4569
4842
|
try {
|
|
4570
4843
|
const ownedPreparation = __using(_stack, preparation);
|
|
4571
4844
|
const session = ownedPreparation.session;
|
|
4572
|
-
|
|
4845
|
+
let prepared;
|
|
4846
|
+
try {
|
|
4847
|
+
prepared = this.prepare(ownerCtx, id, agentOptions, session, signal, stored?.handle);
|
|
4848
|
+
} catch (error) {
|
|
4849
|
+
await stored?.handle.close().catch(() => {
|
|
4850
|
+
});
|
|
4851
|
+
throw error;
|
|
4852
|
+
}
|
|
4573
4853
|
try {
|
|
4574
4854
|
const setupCommit = await raceAbort(setup?.(prepared.agent.ctx), prepared.signal, id);
|
|
4575
4855
|
setupCommit?.commit();
|
|
4856
|
+
await this.appendUnstoredSuffix(stored, session);
|
|
4576
4857
|
return prepared.publish(source);
|
|
4577
4858
|
} catch (error) {
|
|
4578
|
-
await prepared.dispose()
|
|
4859
|
+
await prepared.dispose().catch(() => {
|
|
4860
|
+
});
|
|
4579
4861
|
throw error;
|
|
4580
4862
|
}
|
|
4581
4863
|
} catch (_) {
|
|
@@ -4586,7 +4868,8 @@ var KimiLoop = class extends Service4 {
|
|
|
4586
4868
|
}
|
|
4587
4869
|
/**
|
|
4588
4870
|
* Create an agent and session under one caller-supplied identity, owned by
|
|
4589
|
-
* the accessing fiber.
|
|
4871
|
+
* the accessing fiber. When a persistence backend is mounted, the session's
|
|
4872
|
+
* durable identity is stored before publication.
|
|
4590
4873
|
* @param ownerCtx - caller context that structurally owns the lifecycle.
|
|
4591
4874
|
* @param options - identities, session seed/metadata, loop options, setup, and cancellation.
|
|
4592
4875
|
* @returns the published handle.
|
|
@@ -4594,20 +4877,73 @@ var KimiLoop = class extends Service4 {
|
|
|
4594
4877
|
async createAgent(ownerCtx, options) {
|
|
4595
4878
|
const preparation = SessionPreparation4.create(this.runtime.ctx.sessions.prepare(options.sessionId, {
|
|
4596
4879
|
...options.seed === void 0 ? {} : { seed: options.seed },
|
|
4597
|
-
...options.meta === void 0 ? {} : { meta: options.meta }
|
|
4880
|
+
...options.meta === void 0 ? {} : { meta: options.meta },
|
|
4881
|
+
...options.inheritedEventCount === void 0 ? {} : { inheritedEventCount: options.inheritedEventCount }
|
|
4598
4882
|
}));
|
|
4599
|
-
const published =
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4883
|
+
const published = (async () => {
|
|
4884
|
+
let stored;
|
|
4885
|
+
try {
|
|
4886
|
+
stored = options.signal === void 0 ? await this.createStoredSession(preparation.session) : await raceAbortCall(
|
|
4887
|
+
() => this.createStoredSession(preparation.session, options.signal),
|
|
4888
|
+
options.signal,
|
|
4889
|
+
options.sessionId,
|
|
4890
|
+
(abandoned) => {
|
|
4891
|
+
void abandoned?.handle.close().catch(() => {
|
|
4892
|
+
});
|
|
4893
|
+
}
|
|
4894
|
+
);
|
|
4895
|
+
} catch (error) {
|
|
4896
|
+
preparation[Symbol.dispose]();
|
|
4897
|
+
throw error;
|
|
4898
|
+
}
|
|
4899
|
+
return this.setupAndPublish(
|
|
4900
|
+
ownerCtx,
|
|
4901
|
+
options.sessionId,
|
|
4902
|
+
preparation,
|
|
4903
|
+
options.agentOptions ?? {},
|
|
4904
|
+
options.setup,
|
|
4905
|
+
options.signal,
|
|
4906
|
+
"startup",
|
|
4907
|
+
stored
|
|
4908
|
+
);
|
|
4909
|
+
})();
|
|
4608
4910
|
this.ownership.trackWrapper(published);
|
|
4609
4911
|
return published;
|
|
4610
4912
|
}
|
|
4913
|
+
/**
|
|
4914
|
+
* Take a fresh session's write ownership when persistence is mounted.
|
|
4915
|
+
* Nothing is appended here: the constructor seed (which never re-emits
|
|
4916
|
+
* through `session/event`) is stored by {@link appendUnstoredSuffix} at the
|
|
4917
|
+
* publication commit point, so a failed or cancelled setup closes an
|
|
4918
|
+
* unmaterialized handle and leaves no stored residue — the same id can be
|
|
4919
|
+
* created again.
|
|
4920
|
+
* @param session - the unpublished session to store.
|
|
4921
|
+
* @param signal - optional cancellation forwarded to the backend create.
|
|
4922
|
+
* @returns the owned handle and stored cursor, or `undefined` without a backend.
|
|
4923
|
+
*/
|
|
4924
|
+
async createStoredSession(session, signal) {
|
|
4925
|
+
const persistence = this.runtime.ctx.get("sessionPersistence");
|
|
4926
|
+
if (persistence === void 0) return void 0;
|
|
4927
|
+
const handle = await persistence.create(session.header, {
|
|
4928
|
+
inheritedEventCount: session.inheritedEventCount,
|
|
4929
|
+
...signal === void 0 ? {} : { signal }
|
|
4930
|
+
});
|
|
4931
|
+
return { handle, storedCount: 0 };
|
|
4932
|
+
}
|
|
4933
|
+
/**
|
|
4934
|
+
* Durably store the session events appended since the last stored cursor.
|
|
4935
|
+
* Pre-publication appends (constructor seed markers, setup-window events)
|
|
4936
|
+
* never re-emit through `session/event`, so publication must flush them
|
|
4937
|
+
* through the handle before live events start routing into it.
|
|
4938
|
+
* @param stored - the session's owned handle and stored cursor, if any.
|
|
4939
|
+
* @param session - the unpublished session whose suffix is stored.
|
|
4940
|
+
*/
|
|
4941
|
+
async appendUnstoredSuffix(stored, session) {
|
|
4942
|
+
if (stored === void 0) return;
|
|
4943
|
+
const suffix = session.snapshotEvents(SessionLogOffset4(stored.storedCount));
|
|
4944
|
+
if (suffix.length > 0) await stored.handle.append(suffix);
|
|
4945
|
+
stored.storedCount += suffix.length;
|
|
4946
|
+
}
|
|
4611
4947
|
/**
|
|
4612
4948
|
* Resume an owned agent from the configured persistence service.
|
|
4613
4949
|
* @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
|
|
@@ -4621,11 +4957,10 @@ var KimiLoop = class extends Service4 {
|
|
|
4621
4957
|
}
|
|
4622
4958
|
return this.resumeWith(ownerCtx, persistence, options);
|
|
4623
4959
|
}
|
|
4624
|
-
/** Resume through an explicit persistence
|
|
4625
|
-
|
|
4960
|
+
/** Resume through an explicit persistence service. */
|
|
4961
|
+
resumeWith(ownerCtx, persistence, options) {
|
|
4626
4962
|
const id = options.resumeSessionId;
|
|
4627
|
-
|
|
4628
|
-
try {
|
|
4963
|
+
const published = (async () => {
|
|
4629
4964
|
const ownerAbort = new AbortController();
|
|
4630
4965
|
const unfollowOwner = ownerCtx.effect(() => () => {
|
|
4631
4966
|
ownerAbort.abort(new Error(`agent "${id}" setup aborted: owner disposed during setup`));
|
|
@@ -4635,32 +4970,56 @@ var KimiLoop = class extends Service4 {
|
|
|
4635
4970
|
ownerAbort.signal,
|
|
4636
4971
|
this.ownership.signal
|
|
4637
4972
|
]);
|
|
4973
|
+
let handle;
|
|
4974
|
+
let stored;
|
|
4975
|
+
let preparation;
|
|
4638
4976
|
try {
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4977
|
+
try {
|
|
4978
|
+
handle = await raceAbortCall(
|
|
4979
|
+
() => persistence.open(id, "write", { signal: fused }),
|
|
4980
|
+
fused,
|
|
4981
|
+
id,
|
|
4982
|
+
(abandoned) => {
|
|
4983
|
+
void abandoned.close();
|
|
4984
|
+
}
|
|
4985
|
+
);
|
|
4986
|
+
const persisted = await handle.read(0, void 0, { signal: fused });
|
|
4987
|
+
fused.throwIfAborted();
|
|
4988
|
+
const closers = interruptedTurnClosers4(persisted);
|
|
4989
|
+
if (closers.length > 0) await handle.append(closers);
|
|
4990
|
+
preparation = SessionPreparation4.create(this.runtime.ctx.sessions.prepare(id, {
|
|
4991
|
+
seed: [...persisted, ...closers],
|
|
4992
|
+
meta: structuredClone(handle.header),
|
|
4993
|
+
inheritedEventCount: handle.inheritedEventCount,
|
|
4994
|
+
seedSource: "persistence"
|
|
4995
|
+
}));
|
|
4996
|
+
stored = { handle, storedCount: persisted.length + closers.length };
|
|
4997
|
+
await this.appendUnstoredSuffix(stored, preparation.session);
|
|
4998
|
+
} finally {
|
|
4999
|
+
await unfollowOwner();
|
|
5000
|
+
}
|
|
5001
|
+
ownerCtx.fiber.assertActive();
|
|
5002
|
+
if (!this.ownership.isActive()) throw new Error("agent loop is not active");
|
|
5003
|
+
const owned = stored;
|
|
5004
|
+
handle = void 0;
|
|
5005
|
+
return await this.setupAndPublish(
|
|
5006
|
+
ownerCtx,
|
|
4642
5007
|
id,
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
5008
|
+
preparation,
|
|
5009
|
+
options.agentOptions ?? {},
|
|
5010
|
+
options.setup,
|
|
5011
|
+
options.signal,
|
|
5012
|
+
"resume",
|
|
5013
|
+
owned
|
|
4646
5014
|
);
|
|
4647
5015
|
} finally {
|
|
4648
|
-
|
|
5016
|
+
preparation?.[Symbol.dispose]();
|
|
5017
|
+
await handle?.close().catch(() => {
|
|
5018
|
+
});
|
|
4649
5019
|
}
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
ownerCtx,
|
|
4654
|
-
id,
|
|
4655
|
-
preparation,
|
|
4656
|
-
options.agentOptions ?? {},
|
|
4657
|
-
options.setup,
|
|
4658
|
-
options.signal,
|
|
4659
|
-
"resume"
|
|
4660
|
-
);
|
|
4661
|
-
} finally {
|
|
4662
|
-
preparation?.[Symbol.dispose]();
|
|
4663
|
-
}
|
|
5020
|
+
})();
|
|
5021
|
+
this.ownership.trackWrapper(published);
|
|
5022
|
+
return published;
|
|
4664
5023
|
}
|
|
4665
5024
|
};
|
|
4666
5025
|
|
|
@@ -5102,7 +5461,6 @@ var KIMI_COMMANDS = [
|
|
|
5102
5461
|
builtin("status", "Show the current session runtime state"),
|
|
5103
5462
|
builtin("compact", "Compact the conversation context to free token usage"),
|
|
5104
5463
|
builtin("clear", "Start a fresh session, discarding the current context"),
|
|
5105
|
-
builtin("model", "Switch the LLM model used in the current session"),
|
|
5106
5464
|
builtin("plan", "Toggle plan (read-only exploration) mode"),
|
|
5107
5465
|
builtin("auto", "Toggle auto permission mode"),
|
|
5108
5466
|
builtin("usage", "Show token usage, context, and quota information"),
|
|
@@ -5112,7 +5470,6 @@ var KIMI_COMMANDS = [
|
|
|
5112
5470
|
|
|
5113
5471
|
// src/settings.ts
|
|
5114
5472
|
import z5 from "@deepseek-ai/schemastery";
|
|
5115
|
-
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
5116
5473
|
|
|
5117
5474
|
// src/namespace.ts
|
|
5118
5475
|
var LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL = "agent-loop-engine";
|
|
@@ -5124,7 +5481,7 @@ var LOOP_ENGINE_SETTINGS_SCHEMA = z5.object({
|
|
|
5124
5481
|
showInComposer: z5.boolean().default(true)
|
|
5125
5482
|
});
|
|
5126
5483
|
function loopEngineSettingsNamespace() {
|
|
5127
|
-
return
|
|
5484
|
+
return LOOP_ENGINE_SETTINGS_NAMESPACE_LITERAL;
|
|
5128
5485
|
}
|
|
5129
5486
|
|
|
5130
5487
|
// src/patch-manager.ts
|
|
@@ -5138,6 +5495,8 @@ function renderManagedBlock(engine) {
|
|
|
5138
5495
|
`${MANAGED_BLOCK_BEGIN}${engine} --`,
|
|
5139
5496
|
"- id: agent-loop",
|
|
5140
5497
|
" disabled: true",
|
|
5498
|
+
"- id: command-goal",
|
|
5499
|
+
" disabled: true",
|
|
5141
5500
|
END_MARKER_LINE
|
|
5142
5501
|
].join("\n");
|
|
5143
5502
|
}
|
|
@@ -5199,10 +5558,112 @@ ${block}`;
|
|
|
5199
5558
|
return hasRootEntry(result) ? result : seedEmptyArray(result);
|
|
5200
5559
|
}
|
|
5201
5560
|
|
|
5561
|
+
// src/preset.ts
|
|
5562
|
+
import { mkdir, readFile as readFile4, rename, writeFile } from "node:fs/promises";
|
|
5563
|
+
import { randomUUID } from "node:crypto";
|
|
5564
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
5565
|
+
var HOSTED_PRESET_ID = "loop-engine";
|
|
5566
|
+
var USER_PRESET_DIR = ".agent-presets";
|
|
5567
|
+
var COMPOSITION_FILE = "agent.cordis.yml";
|
|
5568
|
+
var METADATA_FILE = "preset.yml";
|
|
5569
|
+
var SOURCE_PRESET_ID = "standard";
|
|
5570
|
+
var STRIPPED_ROWS = ["skill-filesystem", "tool-skill", "tool-goal", "planning", "compaction"];
|
|
5571
|
+
var MANAGED_HEADER = `# Managed by dsh-loop-engine: the deployment's "${SOURCE_PRESET_ID}" preset minus
|
|
5572
|
+
# the dsh-native command/skill rows a hosted loop engine replaces. Regenerated
|
|
5573
|
+
# from "${SOURCE_PRESET_ID}" on boot \u2014 hand edits are overwritten.
|
|
5574
|
+
`;
|
|
5575
|
+
var MANAGED_METADATA = "name: Hosted Engine\ndescription: Standard preset minus the dsh-native commands and skills a hosted loop engine replaces.\n";
|
|
5576
|
+
function isEntryStart(line) {
|
|
5577
|
+
return line.startsWith("- ");
|
|
5578
|
+
}
|
|
5579
|
+
function entryId(line) {
|
|
5580
|
+
return /^- id:\s*(\S+)\s*$/.exec(line)?.[1];
|
|
5581
|
+
}
|
|
5582
|
+
function stripPresetRows(text, ids = STRIPPED_ROWS) {
|
|
5583
|
+
const lines = text.split("\n");
|
|
5584
|
+
const starts = [];
|
|
5585
|
+
for (const [index, line] of lines.entries()) {
|
|
5586
|
+
if (isEntryStart(line)) starts.push(index);
|
|
5587
|
+
}
|
|
5588
|
+
if (starts.length === 0) return text;
|
|
5589
|
+
const drop = new Set(ids);
|
|
5590
|
+
const entries = [];
|
|
5591
|
+
let heading = lines.slice(0, starts[0]);
|
|
5592
|
+
for (const [index, start] of starts.entries()) {
|
|
5593
|
+
const end = index + 1 < starts.length ? starts[index + 1] : lines.length;
|
|
5594
|
+
const span = lines.slice(start, end);
|
|
5595
|
+
let bodyEnd = span.length;
|
|
5596
|
+
while (bodyEnd > 1) {
|
|
5597
|
+
const line = span[bodyEnd - 1];
|
|
5598
|
+
if (line.trim() !== "" && !line.trimStart().startsWith("#")) break;
|
|
5599
|
+
bodyEnd -= 1;
|
|
5600
|
+
}
|
|
5601
|
+
entries.push({ id: entryId(span[0]), heading, body: span.slice(0, bodyEnd) });
|
|
5602
|
+
heading = span.slice(bodyEnd);
|
|
5603
|
+
}
|
|
5604
|
+
const out = [];
|
|
5605
|
+
out.push(...entries[0].heading);
|
|
5606
|
+
let lastKept = -1;
|
|
5607
|
+
for (const [index, entry] of entries.entries()) {
|
|
5608
|
+
if (entry.id !== void 0 && drop.has(entry.id)) continue;
|
|
5609
|
+
if (index > 0) out.push(...entry.heading);
|
|
5610
|
+
out.push(...entry.body);
|
|
5611
|
+
lastKept = index;
|
|
5612
|
+
}
|
|
5613
|
+
if (lastKept === entries.length - 1) out.push(...heading);
|
|
5614
|
+
if (out.length > 0 && out[out.length - 1] !== "") out.push("");
|
|
5615
|
+
return out.join("\n");
|
|
5616
|
+
}
|
|
5617
|
+
async function writeIfDifferent(path, text) {
|
|
5618
|
+
try {
|
|
5619
|
+
if (await readFile4(path, "utf8") === text) return false;
|
|
5620
|
+
} catch {
|
|
5621
|
+
}
|
|
5622
|
+
await mkdir(dirname4(path), { recursive: true });
|
|
5623
|
+
const tmp = `${path}.tmp-${randomUUID()}`;
|
|
5624
|
+
await writeFile(tmp, text, "utf8");
|
|
5625
|
+
await rename(tmp, path);
|
|
5626
|
+
return true;
|
|
5627
|
+
}
|
|
5628
|
+
async function ensureHostedPreset(dshHome, source) {
|
|
5629
|
+
const composition = await source.read(SOURCE_PRESET_ID);
|
|
5630
|
+
const stripped = `${MANAGED_HEADER}
|
|
5631
|
+
${stripPresetRows(composition)}`;
|
|
5632
|
+
const dir = join7(dshHome, USER_PRESET_DIR, HOSTED_PRESET_ID);
|
|
5633
|
+
const compositionChanged = await writeIfDifferent(join7(dir, COMPOSITION_FILE), stripped);
|
|
5634
|
+
const metadataChanged = await writeIfDifferent(join7(dir, METADATA_FILE), MANAGED_METADATA);
|
|
5635
|
+
return compositionChanged || metadataChanged;
|
|
5636
|
+
}
|
|
5637
|
+
|
|
5638
|
+
// src/provider-route.ts
|
|
5639
|
+
import { LlmAdapter, LlmError as LlmError5 } from "@deepseek-ai/dsh-llm";
|
|
5640
|
+
var HOSTED_PROVIDER_ROUTES = {
|
|
5641
|
+
"claude-code": PROVIDER,
|
|
5642
|
+
codex: PROVIDER2,
|
|
5643
|
+
pi: PROVIDER3,
|
|
5644
|
+
kimi: PROVIDER4
|
|
5645
|
+
};
|
|
5646
|
+
var HostedEngineRouteAdapter = class extends LlmAdapter {
|
|
5647
|
+
/**
|
|
5648
|
+
* @param label - the provider route label this placeholder serves.
|
|
5649
|
+
*/
|
|
5650
|
+
constructor(label) {
|
|
5651
|
+
super();
|
|
5652
|
+
this.label = label;
|
|
5653
|
+
}
|
|
5654
|
+
label;
|
|
5655
|
+
stream(_options) {
|
|
5656
|
+
throw new LlmError5(
|
|
5657
|
+
`provider "${this.label}" is a hosted loop engine route, not a model endpoint`,
|
|
5658
|
+
"HOSTED_ENGINE_ROUTE"
|
|
5659
|
+
);
|
|
5660
|
+
}
|
|
5661
|
+
};
|
|
5662
|
+
|
|
5202
5663
|
// src/commands.ts
|
|
5203
5664
|
import { readdirSync, readFileSync as readFileSync2 } from "node:fs";
|
|
5204
5665
|
import { homedir as homedir4 } from "node:os";
|
|
5205
|
-
import { join as
|
|
5666
|
+
import { join as join8 } from "node:path";
|
|
5206
5667
|
import { createUserMessage as createUserMessage6 } from "@deepseek-ai/dsh-llm";
|
|
5207
5668
|
var COMMAND_NAME = /^[a-z][a-z0-9_-]*$/;
|
|
5208
5669
|
function forwardClaudeCodeCommand(name2) {
|
|
@@ -5239,7 +5700,7 @@ function discoverUserSlashCommands() {
|
|
|
5239
5700
|
if (!entry.endsWith(".md")) continue;
|
|
5240
5701
|
const name2 = entry.slice(0, -".md".length);
|
|
5241
5702
|
if (!COMMAND_NAME.test(name2) || seen.has(name2)) continue;
|
|
5242
|
-
const path =
|
|
5703
|
+
const path = join8(userCommandsDir(), entry);
|
|
5243
5704
|
let raw;
|
|
5244
5705
|
try {
|
|
5245
5706
|
raw = readFileSync2(path, "utf8");
|
|
@@ -5254,7 +5715,7 @@ function discoverUserSlashCommands() {
|
|
|
5254
5715
|
return definitions;
|
|
5255
5716
|
}
|
|
5256
5717
|
function userCommandsDir() {
|
|
5257
|
-
return
|
|
5718
|
+
return join8(homedir4(), ".claude", "commands");
|
|
5258
5719
|
}
|
|
5259
5720
|
function commandDescription(raw) {
|
|
5260
5721
|
const trimmed = raw.trim();
|
|
@@ -5281,7 +5742,7 @@ function commandDescription(raw) {
|
|
|
5281
5742
|
|
|
5282
5743
|
// src/engine-codex/skills.ts
|
|
5283
5744
|
import { homedir as homedir5 } from "node:os";
|
|
5284
|
-
import { join as
|
|
5745
|
+
import { join as join9 } from "node:path";
|
|
5285
5746
|
var PROVIDER_NAME3 = "codex";
|
|
5286
5747
|
var CODEX_PROJECT_RANK = 140;
|
|
5287
5748
|
var CODEX_USER_RANK = 160;
|
|
@@ -5299,7 +5760,7 @@ var CodexSkillProvider = class {
|
|
|
5299
5760
|
const paths = await collectProjectContextFiles(cwd, CODEX_CONTEXT_POLICY);
|
|
5300
5761
|
if (await anySourceNonEmpty(paths)) candidates.push(this.agentsCandidate(paths, CODEX_PROJECT_RANK));
|
|
5301
5762
|
}
|
|
5302
|
-
const userPath =
|
|
5763
|
+
const userPath = join9(homedir5(), ".codex", "AGENTS.md");
|
|
5303
5764
|
if (await fileNonEmpty(userPath)) candidates.push(this.agentsCandidate([userPath], CODEX_USER_RANK));
|
|
5304
5765
|
if (this.control.signal.aborted) return [];
|
|
5305
5766
|
return candidates;
|
|
@@ -5338,9 +5799,9 @@ var CodexSkillProvider = class {
|
|
|
5338
5799
|
};
|
|
5339
5800
|
|
|
5340
5801
|
// src/engine-pi/skills.ts
|
|
5341
|
-
import { readdir as readdir3, readFile as
|
|
5802
|
+
import { readdir as readdir3, readFile as readFile5, stat as stat4 } from "node:fs/promises";
|
|
5342
5803
|
import { homedir as homedir6 } from "node:os";
|
|
5343
|
-
import { dirname as
|
|
5804
|
+
import { dirname as dirname5, join as join10, resolve as resolve4 } from "node:path";
|
|
5344
5805
|
var PROVIDER_NAME4 = "pi";
|
|
5345
5806
|
var PI_AGENTS_PROJECT_RANK = 140;
|
|
5346
5807
|
var PI_SKILL_PROJECT_RANK = 150;
|
|
@@ -5353,7 +5814,7 @@ var PI_CONTEXT_POLICY = {
|
|
|
5353
5814
|
function piAgentDir() {
|
|
5354
5815
|
const override = process.env.PI_CODING_AGENT_DIR;
|
|
5355
5816
|
if (override !== void 0 && override.length > 0) return resolve4(override);
|
|
5356
|
-
return
|
|
5817
|
+
return join10(homedir6(), ".pi", "agent");
|
|
5357
5818
|
}
|
|
5358
5819
|
var PiSkillProvider = class {
|
|
5359
5820
|
constructor(control) {
|
|
@@ -5369,13 +5830,13 @@ var PiSkillProvider = class {
|
|
|
5369
5830
|
const contextPaths = await collectProjectContextFiles(cwd, PI_CONTEXT_POLICY);
|
|
5370
5831
|
if (await anySourceNonEmpty(contextPaths)) candidates.push(this.agentsCandidate(contextPaths, PI_AGENTS_PROJECT_RANK));
|
|
5371
5832
|
for (const dir of projectDirs) {
|
|
5372
|
-
await this.collectSkillsDir(
|
|
5833
|
+
await this.collectSkillsDir(join10(dir, ".pi", "skills"), PI_SKILL_PROJECT_RANK, candidates);
|
|
5373
5834
|
}
|
|
5374
5835
|
}
|
|
5375
5836
|
const userAgentDir = piAgentDir();
|
|
5376
|
-
const userContext =
|
|
5837
|
+
const userContext = join10(userAgentDir, "AGENTS.md");
|
|
5377
5838
|
if (await fileNonEmpty(userContext)) candidates.push(this.agentsCandidate([userContext], PI_AGENTS_USER_RANK));
|
|
5378
|
-
await this.collectSkillsDir(
|
|
5839
|
+
await this.collectSkillsDir(join10(userAgentDir, "skills"), PI_SKILL_USER_RANK, candidates);
|
|
5379
5840
|
if (this.control.signal.aborted) return [];
|
|
5380
5841
|
return candidates;
|
|
5381
5842
|
}
|
|
@@ -5393,7 +5854,7 @@ var PiSkillProvider = class {
|
|
|
5393
5854
|
provider: this.name,
|
|
5394
5855
|
content: parsed.content,
|
|
5395
5856
|
path: locator.path,
|
|
5396
|
-
resourceBase: { kind: "directory", path:
|
|
5857
|
+
resourceBase: { kind: "directory", path: dirname5(locator.path) }
|
|
5397
5858
|
};
|
|
5398
5859
|
}
|
|
5399
5860
|
const content = await readSources(locator.paths);
|
|
@@ -5434,11 +5895,11 @@ var PiSkillProvider = class {
|
|
|
5434
5895
|
return;
|
|
5435
5896
|
}
|
|
5436
5897
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5437
|
-
const entryPath =
|
|
5898
|
+
const entryPath = join10(skillsDir, entry.name);
|
|
5438
5899
|
const info = await stat4(entryPath).catch(() => void 0);
|
|
5439
5900
|
if (info === void 0) continue;
|
|
5440
5901
|
if (info.isDirectory()) {
|
|
5441
|
-
const path =
|
|
5902
|
+
const path = join10(entryPath, "SKILL.md");
|
|
5442
5903
|
const parsed2 = await this.tryParse(path);
|
|
5443
5904
|
if (parsed2 === void 0) continue;
|
|
5444
5905
|
candidates.push(this.skillCandidate(parsed2, path, rank, entryPath));
|
|
@@ -5468,7 +5929,7 @@ var PiSkillProvider = class {
|
|
|
5468
5929
|
/** Parse one SKILL.md file, or `undefined` when it is unreadable or invalid. */
|
|
5469
5930
|
async tryParse(path) {
|
|
5470
5931
|
try {
|
|
5471
|
-
const raw = await
|
|
5932
|
+
const raw = await readFile5(path, { encoding: "utf8" });
|
|
5472
5933
|
return parseSkillFile(raw);
|
|
5473
5934
|
} catch {
|
|
5474
5935
|
return void 0;
|
|
@@ -5498,7 +5959,7 @@ var Config5 = z6.object({
|
|
|
5498
5959
|
});
|
|
5499
5960
|
function resolvePatchPath(config) {
|
|
5500
5961
|
if (config.patchPath !== void 0 && config.patchPath !== "") return config.patchPath;
|
|
5501
|
-
return
|
|
5962
|
+
return join11(
|
|
5502
5963
|
resolveDshHome(),
|
|
5503
5964
|
"profiles",
|
|
5504
5965
|
config.profile ?? "web",
|
|
@@ -5510,21 +5971,21 @@ function isMissing(error) {
|
|
|
5510
5971
|
}
|
|
5511
5972
|
async function readPatchOrUndefined(path) {
|
|
5512
5973
|
try {
|
|
5513
|
-
return await
|
|
5974
|
+
return await readFile6(path, "utf8");
|
|
5514
5975
|
} catch (error) {
|
|
5515
5976
|
if (isMissing(error)) return void 0;
|
|
5516
5977
|
throw error;
|
|
5517
5978
|
}
|
|
5518
5979
|
}
|
|
5519
5980
|
async function writePatchFile(path, text) {
|
|
5520
|
-
await
|
|
5521
|
-
const tmp = `${path}.tmp-${
|
|
5522
|
-
await
|
|
5523
|
-
await
|
|
5981
|
+
await mkdir2(dirname6(path), { recursive: true });
|
|
5982
|
+
const tmp = `${path}.tmp-${randomUUID2()}`;
|
|
5983
|
+
await writeFile2(tmp, text, "utf8");
|
|
5984
|
+
await rename2(tmp, path);
|
|
5524
5985
|
}
|
|
5525
5986
|
function writePatchFileSync(path, text) {
|
|
5526
|
-
mkdirSync(
|
|
5527
|
-
const tmp = `${path}.tmp-${
|
|
5987
|
+
mkdirSync(dirname6(path), { recursive: true });
|
|
5988
|
+
const tmp = `${path}.tmp-${randomUUID2()}`;
|
|
5528
5989
|
writeFileSync(tmp, text, "utf8");
|
|
5529
5990
|
renameSync(tmp, path);
|
|
5530
5991
|
}
|
|
@@ -5579,6 +6040,9 @@ function kimiConfig(config) {
|
|
|
5579
6040
|
function apply(ctx, config) {
|
|
5580
6041
|
const patchPath = resolvePatchPath(config);
|
|
5581
6042
|
let fileEngine = currentEngineOf(readPatchFileSync(patchPath));
|
|
6043
|
+
const AGENT_PRESETS_NS = "agent-presets";
|
|
6044
|
+
const PRESET_DEFAULT_ATTEMPTS = 30;
|
|
6045
|
+
const PRESET_DEFAULT_RETRY_MS = 100;
|
|
5582
6046
|
let engineFiber;
|
|
5583
6047
|
let mountedEngine;
|
|
5584
6048
|
let commandDisposers;
|
|
@@ -5591,6 +6055,105 @@ function apply(ctx, config) {
|
|
|
5591
6055
|
mountRetry = void 0;
|
|
5592
6056
|
}
|
|
5593
6057
|
};
|
|
6058
|
+
let savedPresetDefault;
|
|
6059
|
+
let presetRetry;
|
|
6060
|
+
const CLEAR_PRESET_RETRY = () => {
|
|
6061
|
+
if (presetRetry !== void 0) {
|
|
6062
|
+
clearTimeout(presetRetry);
|
|
6063
|
+
presetRetry = void 0;
|
|
6064
|
+
}
|
|
6065
|
+
};
|
|
6066
|
+
const ROUTE_ATTEMPTS = 30;
|
|
6067
|
+
const ROUTE_RETRY_MS = 100;
|
|
6068
|
+
let routeHandle;
|
|
6069
|
+
let routeEngine;
|
|
6070
|
+
let routeRetry;
|
|
6071
|
+
const CLEAR_ROUTE_RETRY = () => {
|
|
6072
|
+
if (routeRetry !== void 0) {
|
|
6073
|
+
clearTimeout(routeRetry);
|
|
6074
|
+
routeRetry = void 0;
|
|
6075
|
+
}
|
|
6076
|
+
};
|
|
6077
|
+
const releaseRoute = () => {
|
|
6078
|
+
CLEAR_ROUTE_RETRY();
|
|
6079
|
+
const handle = routeHandle;
|
|
6080
|
+
routeHandle = void 0;
|
|
6081
|
+
routeEngine = void 0;
|
|
6082
|
+
handle?.();
|
|
6083
|
+
};
|
|
6084
|
+
const mountProviderRoute = (engine, attempt = 0) => {
|
|
6085
|
+
if (engine === "in-process") return;
|
|
6086
|
+
if (routeEngine === engine && routeHandle !== void 0) return;
|
|
6087
|
+
CLEAR_ROUTE_RETRY();
|
|
6088
|
+
const label = HOSTED_PROVIDER_ROUTES[engine];
|
|
6089
|
+
const llm = ctx.get("llm");
|
|
6090
|
+
if (llm === void 0) {
|
|
6091
|
+
if (attempt < ROUTE_ATTEMPTS) {
|
|
6092
|
+
routeRetry = setTimeout(() => {
|
|
6093
|
+
mountProviderRoute(engine, attempt + 1);
|
|
6094
|
+
}, ROUTE_RETRY_MS);
|
|
6095
|
+
}
|
|
6096
|
+
return;
|
|
6097
|
+
}
|
|
6098
|
+
try {
|
|
6099
|
+
routeHandle = llm.registerAdapter([label], new HostedEngineRouteAdapter(label));
|
|
6100
|
+
routeEngine = engine;
|
|
6101
|
+
} catch (error) {
|
|
6102
|
+
if (error instanceof Error && error.message.includes("already registered")) {
|
|
6103
|
+
ctx.logger.warn(`loop-engine: provider route "${label}" is already served by another adapter`);
|
|
6104
|
+
return;
|
|
6105
|
+
}
|
|
6106
|
+
ctx.logger.error(`loop-engine: provider route "${label}" registration failed: ${String(error)}`);
|
|
6107
|
+
}
|
|
6108
|
+
};
|
|
6109
|
+
const mutatePresetDefault = (op, attempt = 0) => {
|
|
6110
|
+
const settings = ctx.get("settings");
|
|
6111
|
+
if (settings === void 0) return;
|
|
6112
|
+
settings.mutate(AGENT_PRESETS_NS, [op]).then(() => void 0, (error) => {
|
|
6113
|
+
if (error instanceof Error && error.message.includes("not registered") && attempt < PRESET_DEFAULT_ATTEMPTS) {
|
|
6114
|
+
presetRetry = setTimeout(() => {
|
|
6115
|
+
mutatePresetDefault(op, attempt + 1);
|
|
6116
|
+
}, PRESET_DEFAULT_RETRY_MS);
|
|
6117
|
+
return;
|
|
6118
|
+
}
|
|
6119
|
+
ctx.logger.error(`loop-engine: preset default switch failed: ${String(error)}`);
|
|
6120
|
+
});
|
|
6121
|
+
};
|
|
6122
|
+
const restorePresetDefault = (attempt = 0) => {
|
|
6123
|
+
const presets = ctx.get("agentPresets");
|
|
6124
|
+
if (presets === void 0) return;
|
|
6125
|
+
if (presets.defaultId === HOSTED_PRESET_ID) {
|
|
6126
|
+
const saved = savedPresetDefault;
|
|
6127
|
+
savedPresetDefault = void 0;
|
|
6128
|
+
mutatePresetDefault(saved === void 0 ? { op: "unset", path: ["default"] } : { op: "set", path: ["default"], value: saved });
|
|
6129
|
+
return;
|
|
6130
|
+
}
|
|
6131
|
+
if (attempt < PRESET_DEFAULT_ATTEMPTS) {
|
|
6132
|
+
presetRetry = setTimeout(() => {
|
|
6133
|
+
restorePresetDefault(attempt + 1);
|
|
6134
|
+
}, PRESET_DEFAULT_RETRY_MS);
|
|
6135
|
+
}
|
|
6136
|
+
};
|
|
6137
|
+
const steerPresetDefault = (engine) => {
|
|
6138
|
+
const presets = ctx.get("agentPresets");
|
|
6139
|
+
if (presets === void 0) return;
|
|
6140
|
+
if (engine === "in-process") {
|
|
6141
|
+
restorePresetDefault();
|
|
6142
|
+
return;
|
|
6143
|
+
}
|
|
6144
|
+
void (async () => {
|
|
6145
|
+
try {
|
|
6146
|
+
await ensureHostedPreset(resolveDshHome(), presets);
|
|
6147
|
+
} catch (error) {
|
|
6148
|
+
ctx.logger.error(`loop-engine: hosted preset authoring failed: ${String(error)}`);
|
|
6149
|
+
return;
|
|
6150
|
+
}
|
|
6151
|
+
const current = presets.defaultId;
|
|
6152
|
+
if (current === HOSTED_PRESET_ID) return;
|
|
6153
|
+
savedPresetDefault = current;
|
|
6154
|
+
mutatePresetDefault({ op: "set", path: ["default"], value: HOSTED_PRESET_ID });
|
|
6155
|
+
})();
|
|
6156
|
+
};
|
|
5594
6157
|
const cleanupEngineRegistrations = () => {
|
|
5595
6158
|
if (commandDisposers !== void 0) {
|
|
5596
6159
|
for (const dispose of commandDisposers) dispose();
|
|
@@ -5674,6 +6237,7 @@ function apply(ctx, config) {
|
|
|
5674
6237
|
hostFactory("kimi", () => ctx.plugin(KimiLoop, kimiConfig(config)));
|
|
5675
6238
|
};
|
|
5676
6239
|
const mountEngine = (engine) => {
|
|
6240
|
+
mountProviderRoute(engine);
|
|
5677
6241
|
if (engine === "claude-code") mountClaude();
|
|
5678
6242
|
else if (engine === "codex") mountCodex();
|
|
5679
6243
|
else if (engine === "pi") mountPi();
|
|
@@ -5683,6 +6247,7 @@ function apply(ctx, config) {
|
|
|
5683
6247
|
const fiber = engineFiber;
|
|
5684
6248
|
mountAttempts = 0;
|
|
5685
6249
|
CLEAR_RETRY();
|
|
6250
|
+
releaseRoute();
|
|
5686
6251
|
cleanupEngineRegistrations();
|
|
5687
6252
|
mountedEngine = void 0;
|
|
5688
6253
|
if (fiber === void 0) return;
|
|
@@ -5692,27 +6257,36 @@ function apply(ctx, config) {
|
|
|
5692
6257
|
}, () => void 0);
|
|
5693
6258
|
};
|
|
5694
6259
|
mountEngine(fileEngine);
|
|
5695
|
-
|
|
6260
|
+
steerPresetDefault(fileEngine);
|
|
6261
|
+
ctx.effect(() => () => {
|
|
6262
|
+
CLEAR_RETRY();
|
|
6263
|
+
CLEAR_PRESET_RETRY();
|
|
6264
|
+
releaseRoute();
|
|
6265
|
+
}, "loop-engine: retry cleanup");
|
|
5696
6266
|
let source;
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
6267
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
6268
|
+
settingsCtx.settings.installSection(ctx, loopEngineSettingsNamespace(), LOOP_ENGINE_SETTINGS_SCHEMA, { engine: fileEngine, showInComposer: true }, {
|
|
6269
|
+
setSource: (current) => {
|
|
6270
|
+
source = current;
|
|
6271
|
+
},
|
|
6272
|
+
onChange: () => {
|
|
6273
|
+
const next = source().engine;
|
|
6274
|
+
if (next === fileEngine) return;
|
|
6275
|
+
if (mountedEngine !== next) {
|
|
6276
|
+
unmountEngine();
|
|
6277
|
+
mountEngine(next);
|
|
6278
|
+
}
|
|
6279
|
+
try {
|
|
6280
|
+
const updated = applyManagedBlock(readPatchFileSync(patchPath), next);
|
|
6281
|
+
writePatchFileSync(patchPath, updated);
|
|
6282
|
+
fileEngine = next;
|
|
6283
|
+
} catch (error) {
|
|
6284
|
+
ctx.logger.error(`loop-engine: managed block write failed: ${String(error)}`);
|
|
6285
|
+
return;
|
|
6286
|
+
}
|
|
6287
|
+
steerPresetDefault(next);
|
|
5714
6288
|
}
|
|
5715
|
-
}
|
|
6289
|
+
});
|
|
5716
6290
|
});
|
|
5717
6291
|
}
|
|
5718
6292
|
export {
|