@qwen-code/qwen-code 0.15.12-preview.2 → 0.15.12-preview.3
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/bundled/qc-helper/docs/configuration/settings.md +10 -10
- package/bundled/qc-helper/docs/features/lsp.md +87 -10
- package/bundled/qc-helper/docs/qwen-serve.md +46 -15
- package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +11 -11
- package/bundled/stuck/SKILL.md +124 -0
- package/chunks/{agent-UQY6A6OS.js → agent-LIAWUWAO.js} +3 -3
- package/chunks/{ca-VQSV6JHA.js → ca-S3XJMT6P.js} +26 -0
- package/chunks/{chunk-SOIEFHIK.js → chunk-5QQ5FGTU.js} +0 -99
- package/chunks/{chunk-2WFU3IUH.js → chunk-AJSOD5IR.js} +4417 -2250
- package/chunks/{chunk-MXBWOU2L.js → chunk-AOJ3BBY7.js} +12 -12
- package/chunks/{chunk-FSYKVGER.js → chunk-GC5RXNL2.js} +1 -1
- package/chunks/{chunk-PCL3EJGY.js → chunk-XLQ4E5PS.js} +3918 -3677
- package/chunks/{contextCommand-MQRG6RMG.js → contextCommand-SVLAZMQL.js} +4 -4
- package/chunks/{de-M2IPQRBS.js → de-MNR4SMAI.js} +26 -0
- package/chunks/{edit-3KCBTA25.js → edit-VNAZBIZR.js} +7 -3
- package/chunks/{en-N5GMPCVT.js → en-NRN4QBAT.js} +27 -0
- package/chunks/{enter-worktree-VWS5QZTU.js → enter-worktree-FOF5YZIV.js} +3 -3
- package/chunks/{exit-worktree-RVXFWAPD.js → exit-worktree-Y6QVAO3C.js} +3 -3
- package/chunks/{exitPlanMode-UL5DILDG.js → exitPlanMode-QZKO7GH7.js} +3 -3
- package/chunks/{fr-BTHRYEXO.js → fr-OFJFHLCR.js} +26 -0
- package/chunks/{geminiContentGenerator-O2OPGHJG.js → geminiContentGenerator-DYHZPKJX.js} +1 -1
- package/chunks/{glob-57BSREPN.js → glob-G7XATELV.js} +3 -3
- package/chunks/{grep-XO5JOC7T.js → grep-4SETMY47.js} +3 -3
- package/chunks/{ja-D63TAEBO.js → ja-V6OQ6VL7.js} +26 -0
- package/chunks/{monitor-BECPGO3K.js → monitor-JTLJBJ7H.js} +21 -12
- package/chunks/{openaiContentGenerator-KEZQHIRM.js → openaiContentGenerator-3H7XOZBW.js} +2 -2
- package/chunks/{pt-XUV7FSKC.js → pt-ZLE6SA4A.js} +26 -0
- package/chunks/{qwenContentGenerator-RPMRXTNH.js → qwenContentGenerator-FAU3QPYO.js} +4 -4
- package/chunks/{read-file-LGHEIQNH.js → read-file-WWUQVNCZ.js} +1 -1
- package/chunks/{ripGrep-6SFSXZ2G.js → ripGrep-WCOAIWL6.js} +3 -3
- package/chunks/{ru-7KHWMN3A.js → ru-A4OHIUNN.js} +26 -0
- package/chunks/{serve-27O2AFE3.js → serve-VJEEEXA6.js} +780 -104
- package/chunks/{shell-J7K5KYCH.js → shell-IAOKGIJ6.js} +3 -3
- package/chunks/{skill-2R7P4ATS.js → skill-NHW6222K.js} +2 -2
- package/chunks/{src-CGEDVW67.js → src-OWV5HVQQ.js} +82 -12
- package/chunks/{tool-search-XOH3ZWVS.js → tool-search-MSJ6SXLI.js} +1 -1
- package/chunks/{write-file-74NQ27Q2.js → write-file-RKCENFZ5.js} +7 -3
- package/chunks/{zh-VGHU6XBB.js → zh-RN3JULHO.js} +27 -0
- package/chunks/{zh-TW-O36Q4V7E.js → zh-TW-XZEHEV5S.js} +27 -0
- package/cli.js +7440 -5056
- package/locales/ca.js +40 -0
- package/locales/de.js +40 -0
- package/locales/en.js +41 -0
- package/locales/fr.js +41 -0
- package/locales/ja.js +39 -0
- package/locales/pt.js +39 -0
- package/locales/ru.js +39 -0
- package/locales/zh-TW.js +40 -0
- package/locales/zh.js +40 -0
- package/package.json +2 -2
|
@@ -13356,6 +13356,36 @@ var SessionNotFoundError = class extends Error {
|
|
|
13356
13356
|
this.sessionId = sessionId;
|
|
13357
13357
|
}
|
|
13358
13358
|
};
|
|
13359
|
+
var RestoreInProgressError = class extends Error {
|
|
13360
|
+
static {
|
|
13361
|
+
__name(this, "RestoreInProgressError");
|
|
13362
|
+
}
|
|
13363
|
+
sessionId;
|
|
13364
|
+
activeAction;
|
|
13365
|
+
requestedAction;
|
|
13366
|
+
constructor(sessionId, activeAction, requestedAction) {
|
|
13367
|
+
super(
|
|
13368
|
+
`Session "${sessionId}" is already being restored via session/${activeAction}; retry session/${requestedAction} after it completes`
|
|
13369
|
+
);
|
|
13370
|
+
this.name = "RestoreInProgressError";
|
|
13371
|
+
this.sessionId = sessionId;
|
|
13372
|
+
this.activeAction = activeAction;
|
|
13373
|
+
this.requestedAction = requestedAction;
|
|
13374
|
+
}
|
|
13375
|
+
};
|
|
13376
|
+
var InvalidSessionScopeError = class extends Error {
|
|
13377
|
+
static {
|
|
13378
|
+
__name(this, "InvalidSessionScopeError");
|
|
13379
|
+
}
|
|
13380
|
+
sessionScope;
|
|
13381
|
+
constructor(sessionScope) {
|
|
13382
|
+
super(
|
|
13383
|
+
`Invalid sessionScope: ${JSON.stringify(sessionScope)}. Expected 'single' or 'thread'.`
|
|
13384
|
+
);
|
|
13385
|
+
this.name = "InvalidSessionScopeError";
|
|
13386
|
+
this.sessionScope = sessionScope;
|
|
13387
|
+
}
|
|
13388
|
+
};
|
|
13359
13389
|
var SessionLimitExceededError = class extends Error {
|
|
13360
13390
|
static {
|
|
13361
13391
|
__name(this, "SessionLimitExceededError");
|
|
@@ -13383,7 +13413,32 @@ var WorkspaceMismatchError = class extends Error {
|
|
|
13383
13413
|
this.requested = safeRequested;
|
|
13384
13414
|
}
|
|
13385
13415
|
};
|
|
13416
|
+
var InvalidClientIdError = class extends Error {
|
|
13417
|
+
static {
|
|
13418
|
+
__name(this, "InvalidClientIdError");
|
|
13419
|
+
}
|
|
13420
|
+
sessionId;
|
|
13421
|
+
clientId;
|
|
13422
|
+
constructor(sessionId, clientId) {
|
|
13423
|
+
super(`Client id "${clientId}" is not registered for session ${sessionId}`);
|
|
13424
|
+
this.name = "InvalidClientIdError";
|
|
13425
|
+
this.sessionId = sessionId;
|
|
13426
|
+
this.clientId = clientId;
|
|
13427
|
+
}
|
|
13428
|
+
};
|
|
13386
13429
|
var MAX_WORKSPACE_PATH_LENGTH = 4096;
|
|
13430
|
+
var MAX_RESOLVED_PERMISSION_RECORDS = 512;
|
|
13431
|
+
function isServeDebugLoggingEnabled() {
|
|
13432
|
+
const value = process.env["QWEN_SERVE_DEBUG"];
|
|
13433
|
+
if (!value) return false;
|
|
13434
|
+
return !["0", "false", "off", "no"].includes(value.trim().toLowerCase());
|
|
13435
|
+
}
|
|
13436
|
+
__name(isServeDebugLoggingEnabled, "isServeDebugLoggingEnabled");
|
|
13437
|
+
function writeServeDebugLine(message) {
|
|
13438
|
+
if (!isServeDebugLoggingEnabled()) return;
|
|
13439
|
+
writeStderrLine(`qwen serve debug: ${message}`);
|
|
13440
|
+
}
|
|
13441
|
+
__name(writeServeDebugLine, "writeServeDebugLine");
|
|
13387
13442
|
var InvalidPermissionOptionError = class extends Error {
|
|
13388
13443
|
static {
|
|
13389
13444
|
__name(this, "InvalidPermissionOptionError");
|
|
@@ -13400,8 +13455,9 @@ var InvalidPermissionOptionError = class extends Error {
|
|
|
13400
13455
|
}
|
|
13401
13456
|
};
|
|
13402
13457
|
var BridgeClient = class {
|
|
13403
|
-
constructor(resolveEntry, registerPending, rollbackPending, permissionTimeoutMs, maxPendingPerSession) {
|
|
13458
|
+
constructor(resolveEntry, resolvePendingRestoreEvents, registerPending, rollbackPending, permissionTimeoutMs, maxPendingPerSession) {
|
|
13404
13459
|
this.resolveEntry = resolveEntry;
|
|
13460
|
+
this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
|
|
13405
13461
|
this.registerPending = registerPending;
|
|
13406
13462
|
this.rollbackPending = rollbackPending;
|
|
13407
13463
|
this.permissionTimeoutMs = permissionTimeoutMs;
|
|
@@ -13459,7 +13515,8 @@ var BridgeClient = class {
|
|
|
13459
13515
|
sessionId: entry.sessionId,
|
|
13460
13516
|
toolCall: params.toolCall,
|
|
13461
13517
|
options: params.options
|
|
13462
|
-
}
|
|
13518
|
+
},
|
|
13519
|
+
...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
|
|
13463
13520
|
});
|
|
13464
13521
|
if (!published) {
|
|
13465
13522
|
this.rollbackPending(requestId);
|
|
@@ -13481,8 +13538,13 @@ var BridgeClient = class {
|
|
|
13481
13538
|
}
|
|
13482
13539
|
async sessionUpdate(params) {
|
|
13483
13540
|
const entry = this.resolveEntry(params.sessionId);
|
|
13484
|
-
|
|
13485
|
-
|
|
13541
|
+
const events = entry?.events ?? this.resolvePendingRestoreEvents(params.sessionId);
|
|
13542
|
+
if (!events) return;
|
|
13543
|
+
events.publish({
|
|
13544
|
+
type: "session_update",
|
|
13545
|
+
data: params,
|
|
13546
|
+
...entry?.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
|
|
13547
|
+
});
|
|
13486
13548
|
}
|
|
13487
13549
|
async writeTextFile(params) {
|
|
13488
13550
|
let realTarget = params.path;
|
|
@@ -13561,7 +13623,7 @@ var DEFAULT_MAX_SESSIONS = 20;
|
|
|
13561
13623
|
var DEFAULT_PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
13562
13624
|
var DEFAULT_MAX_PENDING_PER_SESSION = 64;
|
|
13563
13625
|
function createHttpAcpBridge(opts) {
|
|
13564
|
-
const
|
|
13626
|
+
const defaultSessionScope = opts.sessionScope ?? "single";
|
|
13565
13627
|
let maxSessions;
|
|
13566
13628
|
if (opts.maxSessions === void 0) {
|
|
13567
13629
|
maxSessions = DEFAULT_MAX_SESSIONS;
|
|
@@ -13578,9 +13640,9 @@ function createHttpAcpBridge(opts) {
|
|
|
13578
13640
|
} else {
|
|
13579
13641
|
maxSessions = opts.maxSessions;
|
|
13580
13642
|
}
|
|
13581
|
-
if (
|
|
13643
|
+
if (defaultSessionScope !== "single" && defaultSessionScope !== "thread") {
|
|
13582
13644
|
throw new TypeError(
|
|
13583
|
-
`Invalid sessionScope: ${JSON.stringify(
|
|
13645
|
+
`Invalid sessionScope: ${JSON.stringify(defaultSessionScope)}. Expected 'single' or 'thread'.`
|
|
13584
13646
|
);
|
|
13585
13647
|
}
|
|
13586
13648
|
const channelFactory = opts.channelFactory ?? defaultSpawnChannelFactory;
|
|
@@ -13606,8 +13668,48 @@ function createHttpAcpBridge(opts) {
|
|
|
13606
13668
|
let inFlightChannelSpawn;
|
|
13607
13669
|
const byId = /* @__PURE__ */ new Map();
|
|
13608
13670
|
const pendingPermissions = /* @__PURE__ */ new Map();
|
|
13671
|
+
const resolvedPermissions = /* @__PURE__ */ new Map();
|
|
13672
|
+
const resolvedPermissionOrder = [];
|
|
13609
13673
|
let shuttingDown = false;
|
|
13610
13674
|
const inFlightSpawns = /* @__PURE__ */ new Map();
|
|
13675
|
+
const inFlightRestores = /* @__PURE__ */ new Map();
|
|
13676
|
+
const pendingRestoreEvents = /* @__PURE__ */ new Map();
|
|
13677
|
+
const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID()}`, "createClientId");
|
|
13678
|
+
const registerClient = /* @__PURE__ */ __name((entry, requestedClientId) => {
|
|
13679
|
+
if (requestedClientId && entry.clientIds.has(requestedClientId)) {
|
|
13680
|
+
entry.clientIds.set(
|
|
13681
|
+
requestedClientId,
|
|
13682
|
+
(entry.clientIds.get(requestedClientId) ?? 0) + 1
|
|
13683
|
+
);
|
|
13684
|
+
return requestedClientId;
|
|
13685
|
+
}
|
|
13686
|
+
const clientId = createClientId();
|
|
13687
|
+
entry.clientIds.set(clientId, 1);
|
|
13688
|
+
return clientId;
|
|
13689
|
+
}, "registerClient");
|
|
13690
|
+
const unregisterClient = /* @__PURE__ */ __name((entry, clientId) => {
|
|
13691
|
+
if (clientId === void 0) return;
|
|
13692
|
+
const count = entry.clientIds.get(clientId);
|
|
13693
|
+
if (count === void 0) return;
|
|
13694
|
+
if (count <= 1) {
|
|
13695
|
+
entry.clientIds.delete(clientId);
|
|
13696
|
+
} else {
|
|
13697
|
+
entry.clientIds.set(clientId, count - 1);
|
|
13698
|
+
}
|
|
13699
|
+
}, "unregisterClient");
|
|
13700
|
+
const resolveTrustedClientId = /* @__PURE__ */ __name((entry, clientId) => {
|
|
13701
|
+
if (clientId === void 0) return void 0;
|
|
13702
|
+
if (!entry.clientIds.has(clientId)) {
|
|
13703
|
+
throw new InvalidClientIdError(entry.sessionId, clientId);
|
|
13704
|
+
}
|
|
13705
|
+
return clientId;
|
|
13706
|
+
}, "resolveTrustedClientId");
|
|
13707
|
+
const resolveAnyTrustedClientId = /* @__PURE__ */ __name((clientId) => {
|
|
13708
|
+
for (const entry of byId.values()) {
|
|
13709
|
+
if (entry.clientIds.has(clientId)) return clientId;
|
|
13710
|
+
}
|
|
13711
|
+
throw new InvalidClientIdError("unknown", clientId);
|
|
13712
|
+
}, "resolveAnyTrustedClientId");
|
|
13611
13713
|
const registerPending = /* @__PURE__ */ __name((p) => {
|
|
13612
13714
|
const entry = byId.get(p.sessionId);
|
|
13613
13715
|
if (!entry) {
|
|
@@ -13617,7 +13719,38 @@ function createHttpAcpBridge(opts) {
|
|
|
13617
13719
|
pendingPermissions.set(p.requestId, p);
|
|
13618
13720
|
entry.pendingPermissionIds.add(p.requestId);
|
|
13619
13721
|
}, "registerPending");
|
|
13620
|
-
const
|
|
13722
|
+
const rememberResolvedPermission = /* @__PURE__ */ __name((record) => {
|
|
13723
|
+
if (!resolvedPermissions.has(record.requestId)) {
|
|
13724
|
+
resolvedPermissionOrder.push(record.requestId);
|
|
13725
|
+
}
|
|
13726
|
+
resolvedPermissions.set(record.requestId, record);
|
|
13727
|
+
while (resolvedPermissionOrder.length > MAX_RESOLVED_PERMISSION_RECORDS) {
|
|
13728
|
+
const oldest = resolvedPermissionOrder.shift();
|
|
13729
|
+
if (oldest !== void 0) resolvedPermissions.delete(oldest);
|
|
13730
|
+
}
|
|
13731
|
+
}, "rememberResolvedPermission");
|
|
13732
|
+
const publishPermissionAlreadyResolved = /* @__PURE__ */ __name((record) => {
|
|
13733
|
+
const entry = byId.get(record.sessionId);
|
|
13734
|
+
if (!entry) return;
|
|
13735
|
+
try {
|
|
13736
|
+
writeServeDebugLine(
|
|
13737
|
+
`permission ${JSON.stringify(record.requestId)} for session ${JSON.stringify(record.sessionId)} was already resolved; publishing duplicate-vote notification.`
|
|
13738
|
+
);
|
|
13739
|
+
entry.events.publish({
|
|
13740
|
+
type: "permission_already_resolved",
|
|
13741
|
+
data: {
|
|
13742
|
+
requestId: record.requestId,
|
|
13743
|
+
sessionId: record.sessionId,
|
|
13744
|
+
outcome: record.outcome
|
|
13745
|
+
}
|
|
13746
|
+
});
|
|
13747
|
+
} catch {
|
|
13748
|
+
writeServeDebugLine(
|
|
13749
|
+
`skipped duplicate-vote notification for permission ${JSON.stringify(record.requestId)} during shutdown.`
|
|
13750
|
+
);
|
|
13751
|
+
}
|
|
13752
|
+
}, "publishPermissionAlreadyResolved");
|
|
13753
|
+
const resolvePending = /* @__PURE__ */ __name((requestId, response, originatorClientId) => {
|
|
13621
13754
|
const pending = pendingPermissions.get(requestId);
|
|
13622
13755
|
if (!pending) return false;
|
|
13623
13756
|
pendingPermissions.delete(requestId);
|
|
@@ -13627,11 +13760,17 @@ function createHttpAcpBridge(opts) {
|
|
|
13627
13760
|
try {
|
|
13628
13761
|
entry.events.publish({
|
|
13629
13762
|
type: "permission_resolved",
|
|
13630
|
-
data: { requestId, outcome: response.outcome }
|
|
13763
|
+
data: { requestId, outcome: response.outcome },
|
|
13764
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
13631
13765
|
});
|
|
13632
13766
|
} catch {
|
|
13633
13767
|
}
|
|
13634
13768
|
}
|
|
13769
|
+
rememberResolvedPermission({
|
|
13770
|
+
requestId,
|
|
13771
|
+
sessionId: pending.sessionId,
|
|
13772
|
+
outcome: response.outcome
|
|
13773
|
+
});
|
|
13635
13774
|
pending.resolve(response);
|
|
13636
13775
|
return true;
|
|
13637
13776
|
}, "resolvePending");
|
|
@@ -13656,6 +13795,7 @@ function createHttpAcpBridge(opts) {
|
|
|
13656
13795
|
}
|
|
13657
13796
|
return void 0;
|
|
13658
13797
|
},
|
|
13798
|
+
(sessionId) => sessionId ? pendingRestoreEvents.get(sessionId) : void 0,
|
|
13659
13799
|
registerPending,
|
|
13660
13800
|
(rid) => (
|
|
13661
13801
|
// Roll back a register-then-publish-failed pending so the agent
|
|
@@ -13671,6 +13811,7 @@ function createHttpAcpBridge(opts) {
|
|
|
13671
13811
|
connection,
|
|
13672
13812
|
client,
|
|
13673
13813
|
sessionIds: /* @__PURE__ */ new Set(),
|
|
13814
|
+
pendingRestoreIds: /* @__PURE__ */ new Set(),
|
|
13674
13815
|
isDying: false
|
|
13675
13816
|
};
|
|
13676
13817
|
aliveChannels.add(info);
|
|
@@ -13746,7 +13887,7 @@ function createHttpAcpBridge(opts) {
|
|
|
13746
13887
|
}
|
|
13747
13888
|
}
|
|
13748
13889
|
__name(ensureChannel, "ensureChannel");
|
|
13749
|
-
async function doSpawn(modelServiceId) {
|
|
13890
|
+
async function doSpawn(modelServiceId, effectiveScope, requestedClientId) {
|
|
13750
13891
|
const ci = await ensureChannel();
|
|
13751
13892
|
let newSessionResp;
|
|
13752
13893
|
try {
|
|
@@ -13769,26 +13910,21 @@ function createHttpAcpBridge(opts) {
|
|
|
13769
13910
|
if (shuttingDown) {
|
|
13770
13911
|
throw new Error("HttpAcpBridge is shutting down");
|
|
13771
13912
|
}
|
|
13772
|
-
const entry =
|
|
13773
|
-
|
|
13774
|
-
|
|
13775
|
-
|
|
13776
|
-
|
|
13777
|
-
|
|
13778
|
-
|
|
13779
|
-
modelChangeQueue: Promise.resolve(),
|
|
13780
|
-
pendingPermissionIds: /* @__PURE__ */ new Set(),
|
|
13781
|
-
attachCount: 0,
|
|
13782
|
-
spawnOwnerWantedKill: false
|
|
13783
|
-
};
|
|
13784
|
-
ci.sessionIds.add(entry.sessionId);
|
|
13785
|
-
byId.set(entry.sessionId, entry);
|
|
13786
|
-
if (!defaultEntry) defaultEntry = entry;
|
|
13913
|
+
const entry = createSessionEntry(
|
|
13914
|
+
ci,
|
|
13915
|
+
newSessionResp.sessionId,
|
|
13916
|
+
boundWorkspace
|
|
13917
|
+
);
|
|
13918
|
+
const clientId = registerClient(entry, requestedClientId);
|
|
13919
|
+
if (effectiveScope === "single" && !defaultEntry) defaultEntry = entry;
|
|
13787
13920
|
if (modelServiceId) {
|
|
13788
|
-
await applyModelServiceId(
|
|
13789
|
-
|
|
13790
|
-
|
|
13791
|
-
|
|
13921
|
+
await applyModelServiceId(
|
|
13922
|
+
entry,
|
|
13923
|
+
modelServiceId,
|
|
13924
|
+
initTimeoutMs,
|
|
13925
|
+
clientId
|
|
13926
|
+
).catch(() => {
|
|
13927
|
+
});
|
|
13792
13928
|
}
|
|
13793
13929
|
if (!byId.has(entry.sessionId)) {
|
|
13794
13930
|
throw new Error(
|
|
@@ -13798,11 +13934,12 @@ function createHttpAcpBridge(opts) {
|
|
|
13798
13934
|
return {
|
|
13799
13935
|
sessionId: entry.sessionId,
|
|
13800
13936
|
workspaceCwd: entry.workspaceCwd,
|
|
13801
|
-
attached: false
|
|
13937
|
+
attached: false,
|
|
13938
|
+
clientId
|
|
13802
13939
|
};
|
|
13803
13940
|
}
|
|
13804
13941
|
__name(doSpawn, "doSpawn");
|
|
13805
|
-
async function applyModelServiceId(entry, modelId, timeoutMs) {
|
|
13942
|
+
async function applyModelServiceId(entry, modelId, timeoutMs, originatorClientId) {
|
|
13806
13943
|
const conn = entry.connection;
|
|
13807
13944
|
const transportClosed = getTransportClosedReject(entry);
|
|
13808
13945
|
const work = entry.modelChangeQueue.then(async () => {
|
|
@@ -13820,7 +13957,8 @@ function createHttpAcpBridge(opts) {
|
|
|
13820
13957
|
]);
|
|
13821
13958
|
entry.events.publish({
|
|
13822
13959
|
type: "model_switched",
|
|
13823
|
-
data: { sessionId: entry.sessionId, modelId }
|
|
13960
|
+
data: { sessionId: entry.sessionId, modelId },
|
|
13961
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
13824
13962
|
});
|
|
13825
13963
|
} catch (err) {
|
|
13826
13964
|
entry.events.publish({
|
|
@@ -13829,7 +13967,8 @@ function createHttpAcpBridge(opts) {
|
|
|
13829
13967
|
sessionId: entry.sessionId,
|
|
13830
13968
|
requestedModelId: modelId,
|
|
13831
13969
|
error: err instanceof Error ? err.message : String(err)
|
|
13832
|
-
}
|
|
13970
|
+
},
|
|
13971
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
13833
13972
|
});
|
|
13834
13973
|
throw err;
|
|
13835
13974
|
}
|
|
@@ -13859,6 +13998,213 @@ function createHttpAcpBridge(opts) {
|
|
|
13859
13998
|
}
|
|
13860
13999
|
return entry.transportClosedReject;
|
|
13861
14000
|
}, "getTransportClosedReject");
|
|
14001
|
+
const resolveWorkspaceKey = /* @__PURE__ */ __name((workspaceCwd) => {
|
|
14002
|
+
if (!path.isAbsolute(workspaceCwd)) {
|
|
14003
|
+
throw new Error(
|
|
14004
|
+
`workspaceCwd must be an absolute path; got "${workspaceCwd}"`
|
|
14005
|
+
);
|
|
14006
|
+
}
|
|
14007
|
+
const workspaceKey = workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(workspaceCwd);
|
|
14008
|
+
if (workspaceKey !== boundWorkspace) {
|
|
14009
|
+
throw new WorkspaceMismatchError(boundWorkspace, workspaceKey);
|
|
14010
|
+
}
|
|
14011
|
+
return workspaceKey;
|
|
14012
|
+
}, "resolveWorkspaceKey");
|
|
14013
|
+
const createSessionEntry = /* @__PURE__ */ __name((ci, sessionId, workspaceCwd, events = new EventBus()) => {
|
|
14014
|
+
const entry = {
|
|
14015
|
+
sessionId,
|
|
14016
|
+
workspaceCwd,
|
|
14017
|
+
channel: ci.channel,
|
|
14018
|
+
connection: ci.connection,
|
|
14019
|
+
events,
|
|
14020
|
+
promptQueue: Promise.resolve(),
|
|
14021
|
+
modelChangeQueue: Promise.resolve(),
|
|
14022
|
+
pendingPermissionIds: /* @__PURE__ */ new Set(),
|
|
14023
|
+
clientIds: /* @__PURE__ */ new Map(),
|
|
14024
|
+
attachCount: 0,
|
|
14025
|
+
spawnOwnerWantedKill: false
|
|
14026
|
+
};
|
|
14027
|
+
ci.sessionIds.add(entry.sessionId);
|
|
14028
|
+
byId.set(entry.sessionId, entry);
|
|
14029
|
+
return entry;
|
|
14030
|
+
}, "createSessionEntry");
|
|
14031
|
+
const isAcpSessionResourceNotFound = /* @__PURE__ */ __name((err, sessionId) => {
|
|
14032
|
+
if (!err || typeof err !== "object") return false;
|
|
14033
|
+
const maybe = err;
|
|
14034
|
+
if (maybe.code !== -32002) return false;
|
|
14035
|
+
const expectedUri = `session:${sessionId}`;
|
|
14036
|
+
if (maybe.data && typeof maybe.data === "object" && maybe.data.uri === expectedUri) {
|
|
14037
|
+
return true;
|
|
14038
|
+
}
|
|
14039
|
+
return typeof maybe.message === "string" && maybe.message === `Resource not found: ${expectedUri}`;
|
|
14040
|
+
}, "isAcpSessionResourceNotFound");
|
|
14041
|
+
async function restoreSession(action, req) {
|
|
14042
|
+
if (shuttingDown) {
|
|
14043
|
+
throw new Error("HttpAcpBridge is shutting down");
|
|
14044
|
+
}
|
|
14045
|
+
const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
|
|
14046
|
+
const existing = byId.get(req.sessionId);
|
|
14047
|
+
if (existing) {
|
|
14048
|
+
existing.attachCount++;
|
|
14049
|
+
const clientId = registerClient(existing, req.clientId);
|
|
14050
|
+
return {
|
|
14051
|
+
sessionId: existing.sessionId,
|
|
14052
|
+
workspaceCwd: existing.workspaceCwd,
|
|
14053
|
+
attached: true,
|
|
14054
|
+
clientId,
|
|
14055
|
+
// Late attachers get the same ACP state the original restore
|
|
14056
|
+
// caller saw; spawn-only sessions don't carry a state payload.
|
|
14057
|
+
state: existing.restoreState ?? {}
|
|
14058
|
+
};
|
|
14059
|
+
}
|
|
14060
|
+
const inFlight = inFlightRestores.get(req.sessionId);
|
|
14061
|
+
if (inFlight) {
|
|
14062
|
+
if (action !== inFlight.action) {
|
|
14063
|
+
throw new RestoreInProgressError(
|
|
14064
|
+
req.sessionId,
|
|
14065
|
+
inFlight.action,
|
|
14066
|
+
action
|
|
14067
|
+
);
|
|
14068
|
+
}
|
|
14069
|
+
inFlight.coalesceState.count++;
|
|
14070
|
+
let restored;
|
|
14071
|
+
try {
|
|
14072
|
+
restored = await inFlight.promise;
|
|
14073
|
+
} catch (err) {
|
|
14074
|
+
inFlight.coalesceState.count--;
|
|
14075
|
+
throw err;
|
|
14076
|
+
}
|
|
14077
|
+
const entry = byId.get(restored.sessionId);
|
|
14078
|
+
if (!entry) {
|
|
14079
|
+
inFlight.coalesceState.count--;
|
|
14080
|
+
throw new SessionNotFoundError(
|
|
14081
|
+
restored.sessionId,
|
|
14082
|
+
"the agent child likely crashed during session restore \u2014 retry to restore the session"
|
|
14083
|
+
);
|
|
14084
|
+
}
|
|
14085
|
+
return {
|
|
14086
|
+
...restored,
|
|
14087
|
+
attached: true,
|
|
14088
|
+
clientId: registerClient(entry, req.clientId)
|
|
14089
|
+
};
|
|
14090
|
+
}
|
|
14091
|
+
if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
|
|
14092
|
+
throw new SessionLimitExceededError(maxSessions);
|
|
14093
|
+
}
|
|
14094
|
+
const restoreEvents = new EventBus();
|
|
14095
|
+
let registeredEntry;
|
|
14096
|
+
let ci;
|
|
14097
|
+
const coalesceState = { count: 0 };
|
|
14098
|
+
const promise = (async () => {
|
|
14099
|
+
pendingRestoreEvents.set(req.sessionId, restoreEvents);
|
|
14100
|
+
ci = await ensureChannel();
|
|
14101
|
+
ci.pendingRestoreIds.add(req.sessionId);
|
|
14102
|
+
const transportClosed = ci.channel.exited.then(() => {
|
|
14103
|
+
throw new Error(`agent channel closed during session/${action}`);
|
|
14104
|
+
});
|
|
14105
|
+
transportClosed.catch(() => {
|
|
14106
|
+
});
|
|
14107
|
+
let state;
|
|
14108
|
+
try {
|
|
14109
|
+
if (action === "load") {
|
|
14110
|
+
state = await Promise.race([
|
|
14111
|
+
withTimeout(
|
|
14112
|
+
ci.connection.loadSession({
|
|
14113
|
+
sessionId: req.sessionId,
|
|
14114
|
+
cwd: workspaceKey,
|
|
14115
|
+
// Restore path drops per-request `mcpServers` (matches
|
|
14116
|
+
// `doSpawn`); daemon-wide MCP comes from settings on
|
|
14117
|
+
// the agent side. The SDK's `RestoreSessionRequest`
|
|
14118
|
+
// intentionally has no `mcpServers` field for the
|
|
14119
|
+
// same reason.
|
|
14120
|
+
mcpServers: []
|
|
14121
|
+
}),
|
|
14122
|
+
initTimeoutMs,
|
|
14123
|
+
"loadSession"
|
|
14124
|
+
),
|
|
14125
|
+
transportClosed
|
|
14126
|
+
]);
|
|
14127
|
+
} else {
|
|
14128
|
+
state = await Promise.race([
|
|
14129
|
+
withTimeout(
|
|
14130
|
+
ci.connection.unstable_resumeSession({
|
|
14131
|
+
sessionId: req.sessionId,
|
|
14132
|
+
cwd: workspaceKey,
|
|
14133
|
+
mcpServers: []
|
|
14134
|
+
}),
|
|
14135
|
+
initTimeoutMs,
|
|
14136
|
+
"resumeSession"
|
|
14137
|
+
),
|
|
14138
|
+
transportClosed
|
|
14139
|
+
]);
|
|
14140
|
+
}
|
|
14141
|
+
} catch (err) {
|
|
14142
|
+
restoreEvents.close();
|
|
14143
|
+
if (isAcpSessionResourceNotFound(err, req.sessionId)) {
|
|
14144
|
+
throw new SessionNotFoundError(req.sessionId);
|
|
14145
|
+
}
|
|
14146
|
+
if (ci.sessionIds.size === 0 && ci.pendingRestoreIds.size === 1 && ci.pendingRestoreIds.has(req.sessionId)) {
|
|
14147
|
+
ci.isDying = true;
|
|
14148
|
+
await ci.channel.kill().catch(() => {
|
|
14149
|
+
});
|
|
14150
|
+
}
|
|
14151
|
+
throw err;
|
|
14152
|
+
}
|
|
14153
|
+
if (shuttingDown) {
|
|
14154
|
+
restoreEvents.close();
|
|
14155
|
+
throw new Error("HttpAcpBridge is shutting down");
|
|
14156
|
+
}
|
|
14157
|
+
if (ci.isDying || !aliveChannels.has(ci)) {
|
|
14158
|
+
restoreEvents.close();
|
|
14159
|
+
throw new Error(
|
|
14160
|
+
`Session ${req.sessionId} restored on a closed agent channel`
|
|
14161
|
+
);
|
|
14162
|
+
}
|
|
14163
|
+
const racedEntry = byId.get(req.sessionId);
|
|
14164
|
+
if (racedEntry) {
|
|
14165
|
+
restoreEvents.close();
|
|
14166
|
+
racedEntry.attachCount += 1 + coalesceState.count;
|
|
14167
|
+
const clientId2 = registerClient(racedEntry, req.clientId);
|
|
14168
|
+
return {
|
|
14169
|
+
sessionId: racedEntry.sessionId,
|
|
14170
|
+
workspaceCwd: racedEntry.workspaceCwd,
|
|
14171
|
+
attached: true,
|
|
14172
|
+
clientId: clientId2,
|
|
14173
|
+
state: racedEntry.restoreState ?? {}
|
|
14174
|
+
};
|
|
14175
|
+
}
|
|
14176
|
+
const entry = createSessionEntry(
|
|
14177
|
+
ci,
|
|
14178
|
+
req.sessionId,
|
|
14179
|
+
workspaceKey,
|
|
14180
|
+
restoreEvents
|
|
14181
|
+
);
|
|
14182
|
+
entry.restoreState = state;
|
|
14183
|
+
const clientId = registerClient(entry, req.clientId);
|
|
14184
|
+
entry.attachCount = coalesceState.count;
|
|
14185
|
+
registeredEntry = entry;
|
|
14186
|
+
return {
|
|
14187
|
+
sessionId: entry.sessionId,
|
|
14188
|
+
workspaceCwd: entry.workspaceCwd,
|
|
14189
|
+
attached: false,
|
|
14190
|
+
clientId,
|
|
14191
|
+
state
|
|
14192
|
+
};
|
|
14193
|
+
})().finally(() => {
|
|
14194
|
+
ci?.pendingRestoreIds.delete(req.sessionId);
|
|
14195
|
+
pendingRestoreEvents.delete(req.sessionId);
|
|
14196
|
+
if (!registeredEntry) {
|
|
14197
|
+
restoreEvents.close();
|
|
14198
|
+
}
|
|
14199
|
+
});
|
|
14200
|
+
inFlightRestores.set(req.sessionId, { action, promise, coalesceState });
|
|
14201
|
+
try {
|
|
14202
|
+
return await promise;
|
|
14203
|
+
} finally {
|
|
14204
|
+
inFlightRestores.delete(req.sessionId);
|
|
14205
|
+
}
|
|
14206
|
+
}
|
|
14207
|
+
__name(restoreSession, "restoreSession");
|
|
13862
14208
|
return {
|
|
13863
14209
|
get sessionCount() {
|
|
13864
14210
|
return byId.size;
|
|
@@ -13866,35 +14212,40 @@ function createHttpAcpBridge(opts) {
|
|
|
13866
14212
|
get pendingPermissionCount() {
|
|
13867
14213
|
return pendingPermissions.size;
|
|
13868
14214
|
},
|
|
14215
|
+
async loadSession(req) {
|
|
14216
|
+
return restoreSession("load", req);
|
|
14217
|
+
},
|
|
14218
|
+
async resumeSession(req) {
|
|
14219
|
+
return restoreSession("resume", req);
|
|
14220
|
+
},
|
|
13869
14221
|
async spawnOrAttach(req) {
|
|
13870
14222
|
if (shuttingDown) {
|
|
13871
14223
|
throw new Error("HttpAcpBridge is shutting down");
|
|
13872
14224
|
}
|
|
13873
|
-
|
|
13874
|
-
|
|
13875
|
-
|
|
13876
|
-
);
|
|
13877
|
-
}
|
|
13878
|
-
const workspaceKey = req.workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(req.workspaceCwd);
|
|
13879
|
-
if (workspaceKey !== boundWorkspace) {
|
|
13880
|
-
throw new WorkspaceMismatchError(boundWorkspace, workspaceKey);
|
|
14225
|
+
const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
|
|
14226
|
+
if (req.sessionScope !== void 0 && req.sessionScope !== "single" && req.sessionScope !== "thread") {
|
|
14227
|
+
throw new InvalidSessionScopeError(req.sessionScope);
|
|
13881
14228
|
}
|
|
13882
|
-
|
|
14229
|
+
const effectiveScope = req.sessionScope ?? defaultSessionScope;
|
|
14230
|
+
if (effectiveScope === "single") {
|
|
13883
14231
|
const existing = defaultEntry;
|
|
13884
14232
|
if (existing) {
|
|
13885
14233
|
existing.attachCount++;
|
|
14234
|
+
const clientId = registerClient(existing, req.clientId);
|
|
13886
14235
|
if (req.modelServiceId) {
|
|
13887
14236
|
await applyModelServiceId(
|
|
13888
14237
|
existing,
|
|
13889
14238
|
req.modelServiceId,
|
|
13890
|
-
initTimeoutMs
|
|
14239
|
+
initTimeoutMs,
|
|
14240
|
+
clientId
|
|
13891
14241
|
).catch(() => {
|
|
13892
14242
|
});
|
|
13893
14243
|
}
|
|
13894
14244
|
return {
|
|
13895
14245
|
sessionId: existing.sessionId,
|
|
13896
14246
|
workspaceCwd: existing.workspaceCwd,
|
|
13897
|
-
attached: true
|
|
14247
|
+
attached: true,
|
|
14248
|
+
clientId
|
|
13898
14249
|
};
|
|
13899
14250
|
}
|
|
13900
14251
|
const inFlight = inFlightSpawns.get(workspaceKey);
|
|
@@ -13908,22 +14259,24 @@ function createHttpAcpBridge(opts) {
|
|
|
13908
14259
|
"the agent child likely crashed during initialization \u2014 retry to spawn a new session"
|
|
13909
14260
|
);
|
|
13910
14261
|
}
|
|
14262
|
+
const clientId = registerClient(attachedEntry, req.clientId);
|
|
13911
14263
|
if (req.modelServiceId) {
|
|
13912
14264
|
await applyModelServiceId(
|
|
13913
14265
|
attachedEntry,
|
|
13914
14266
|
req.modelServiceId,
|
|
13915
|
-
initTimeoutMs
|
|
14267
|
+
initTimeoutMs,
|
|
14268
|
+
clientId
|
|
13916
14269
|
).catch(() => {
|
|
13917
14270
|
});
|
|
13918
14271
|
}
|
|
13919
|
-
return { ...session, attached: true };
|
|
14272
|
+
return { ...session, attached: true, clientId };
|
|
13920
14273
|
}
|
|
13921
14274
|
}
|
|
13922
|
-
if (byId.size + inFlightSpawns.size >= maxSessions) {
|
|
14275
|
+
if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
|
|
13923
14276
|
throw new SessionLimitExceededError(maxSessions);
|
|
13924
14277
|
}
|
|
13925
|
-
const promise = doSpawn(req.modelServiceId);
|
|
13926
|
-
const tracker =
|
|
14278
|
+
const promise = doSpawn(req.modelServiceId, effectiveScope, req.clientId);
|
|
14279
|
+
const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID()}`;
|
|
13927
14280
|
inFlightSpawns.set(tracker, promise);
|
|
13928
14281
|
try {
|
|
13929
14282
|
return await promise;
|
|
@@ -13931,9 +14284,13 @@ function createHttpAcpBridge(opts) {
|
|
|
13931
14284
|
inFlightSpawns.delete(tracker);
|
|
13932
14285
|
}
|
|
13933
14286
|
},
|
|
13934
|
-
async sendPrompt(sessionId, req, signal) {
|
|
14287
|
+
async sendPrompt(sessionId, req, signal, context) {
|
|
13935
14288
|
const entry = byId.get(sessionId);
|
|
13936
14289
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
14290
|
+
const originatorClientId = resolveTrustedClientId(
|
|
14291
|
+
entry,
|
|
14292
|
+
context?.clientId
|
|
14293
|
+
);
|
|
13937
14294
|
if (signal?.aborted) {
|
|
13938
14295
|
throw new DOMException("Prompt aborted", "AbortError");
|
|
13939
14296
|
}
|
|
@@ -13942,7 +14299,14 @@ function createHttpAcpBridge(opts) {
|
|
|
13942
14299
|
if (signal?.aborted) {
|
|
13943
14300
|
throw new DOMException("Prompt aborted", "AbortError");
|
|
13944
14301
|
}
|
|
13945
|
-
|
|
14302
|
+
if (originatorClientId === void 0) {
|
|
14303
|
+
delete entry.activePromptOriginatorClientId;
|
|
14304
|
+
} else {
|
|
14305
|
+
entry.activePromptOriginatorClientId = originatorClientId;
|
|
14306
|
+
}
|
|
14307
|
+
const promptPromise = entry.connection.prompt(normalized).finally(() => {
|
|
14308
|
+
delete entry.activePromptOriginatorClientId;
|
|
14309
|
+
});
|
|
13946
14310
|
const racedPromise = Promise.race([
|
|
13947
14311
|
promptPromise,
|
|
13948
14312
|
getTransportClosedReject(entry)
|
|
@@ -13969,9 +14333,10 @@ function createHttpAcpBridge(opts) {
|
|
|
13969
14333
|
);
|
|
13970
14334
|
return result;
|
|
13971
14335
|
},
|
|
13972
|
-
async cancelSession(sessionId, req) {
|
|
14336
|
+
async cancelSession(sessionId, req, context) {
|
|
13973
14337
|
const entry = byId.get(sessionId);
|
|
13974
14338
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
14339
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
13975
14340
|
cancelPendingForSession(sessionId);
|
|
13976
14341
|
const notif = req ? { ...req, sessionId } : { sessionId };
|
|
13977
14342
|
await entry.connection.cancel(notif);
|
|
@@ -13981,17 +14346,69 @@ function createHttpAcpBridge(opts) {
|
|
|
13981
14346
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
13982
14347
|
return entry.events.subscribe(subOpts);
|
|
13983
14348
|
},
|
|
13984
|
-
respondToPermission(requestId, response) {
|
|
14349
|
+
respondToPermission(requestId, response, context) {
|
|
14350
|
+
const pending = pendingPermissions.get(requestId);
|
|
14351
|
+
let originatorClientId;
|
|
14352
|
+
if (context?.clientId !== void 0 && !pending) {
|
|
14353
|
+
resolveAnyTrustedClientId(context.clientId);
|
|
14354
|
+
} else if (pending && context?.clientId !== void 0) {
|
|
14355
|
+
const entry = byId.get(pending.sessionId);
|
|
14356
|
+
if (entry) {
|
|
14357
|
+
originatorClientId = resolveTrustedClientId(entry, context.clientId);
|
|
14358
|
+
} else {
|
|
14359
|
+
resolveAnyTrustedClientId(context.clientId);
|
|
14360
|
+
}
|
|
14361
|
+
}
|
|
14362
|
+
if (!pending) {
|
|
14363
|
+
const record = resolvedPermissions.get(requestId);
|
|
14364
|
+
if (record) {
|
|
14365
|
+
publishPermissionAlreadyResolved(record);
|
|
14366
|
+
}
|
|
14367
|
+
return false;
|
|
14368
|
+
}
|
|
13985
14369
|
if (response.outcome.outcome === "selected") {
|
|
13986
|
-
|
|
13987
|
-
if (pending && !pending.allowedOptionIds.has(response.outcome.optionId)) {
|
|
14370
|
+
if (!pending.allowedOptionIds.has(response.outcome.optionId)) {
|
|
13988
14371
|
throw new InvalidPermissionOptionError(
|
|
13989
14372
|
requestId,
|
|
13990
14373
|
response.outcome.optionId
|
|
13991
14374
|
);
|
|
13992
14375
|
}
|
|
13993
14376
|
}
|
|
13994
|
-
return resolvePending(requestId, response);
|
|
14377
|
+
return resolvePending(requestId, response, originatorClientId);
|
|
14378
|
+
},
|
|
14379
|
+
respondToSessionPermission(sessionId, requestId, response, context) {
|
|
14380
|
+
const entry = byId.get(sessionId);
|
|
14381
|
+
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
14382
|
+
const pending = pendingPermissions.get(requestId);
|
|
14383
|
+
if (!pending) {
|
|
14384
|
+
const record = resolvedPermissions.get(requestId);
|
|
14385
|
+
if (record?.sessionId === sessionId) {
|
|
14386
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
14387
|
+
publishPermissionAlreadyResolved(record);
|
|
14388
|
+
} else if (record) {
|
|
14389
|
+
writeServeDebugLine(
|
|
14390
|
+
`rejected permission vote ${JSON.stringify(requestId)} for session ${JSON.stringify(sessionId)}; request belongs to session ${JSON.stringify(record.sessionId)}.`
|
|
14391
|
+
);
|
|
14392
|
+
}
|
|
14393
|
+
return false;
|
|
14394
|
+
}
|
|
14395
|
+
if (pending.sessionId !== sessionId) {
|
|
14396
|
+
writeServeDebugLine(
|
|
14397
|
+
`rejected permission vote ${JSON.stringify(requestId)} for session ${JSON.stringify(sessionId)}; request belongs to session ${JSON.stringify(pending.sessionId)}.`
|
|
14398
|
+
);
|
|
14399
|
+
return false;
|
|
14400
|
+
}
|
|
14401
|
+
const originatorClientId = resolveTrustedClientId(
|
|
14402
|
+
entry,
|
|
14403
|
+
context?.clientId
|
|
14404
|
+
);
|
|
14405
|
+
if (response.outcome.outcome === "selected" && !pending.allowedOptionIds.has(response.outcome.optionId)) {
|
|
14406
|
+
throw new InvalidPermissionOptionError(
|
|
14407
|
+
requestId,
|
|
14408
|
+
response.outcome.optionId
|
|
14409
|
+
);
|
|
14410
|
+
}
|
|
14411
|
+
return resolvePending(requestId, response, originatorClientId);
|
|
13995
14412
|
},
|
|
13996
14413
|
listWorkspaceSessions(workspaceCwd) {
|
|
13997
14414
|
if (!path.isAbsolute(workspaceCwd)) return [];
|
|
@@ -14008,9 +14425,13 @@ function createHttpAcpBridge(opts) {
|
|
|
14008
14425
|
}
|
|
14009
14426
|
return out;
|
|
14010
14427
|
},
|
|
14011
|
-
async setSessionModel(sessionId, req) {
|
|
14428
|
+
async setSessionModel(sessionId, req, context) {
|
|
14012
14429
|
const entry = byId.get(sessionId);
|
|
14013
14430
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
14431
|
+
const originatorClientId = resolveTrustedClientId(
|
|
14432
|
+
entry,
|
|
14433
|
+
context?.clientId
|
|
14434
|
+
);
|
|
14014
14435
|
const normalized = { ...req, sessionId };
|
|
14015
14436
|
const conn = entry.connection;
|
|
14016
14437
|
const transportClosed = getTransportClosedReject(entry);
|
|
@@ -14039,7 +14460,8 @@ function createHttpAcpBridge(opts) {
|
|
|
14039
14460
|
sessionId: entry.sessionId,
|
|
14040
14461
|
requestedModelId: req.modelId,
|
|
14041
14462
|
error: err instanceof Error ? err.message : String(err)
|
|
14042
|
-
}
|
|
14463
|
+
},
|
|
14464
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
14043
14465
|
});
|
|
14044
14466
|
} catch {
|
|
14045
14467
|
}
|
|
@@ -14048,7 +14470,8 @@ function createHttpAcpBridge(opts) {
|
|
|
14048
14470
|
try {
|
|
14049
14471
|
entry.events.publish({
|
|
14050
14472
|
type: "model_switched",
|
|
14051
|
-
data: { sessionId: entry.sessionId, modelId: req.modelId }
|
|
14473
|
+
data: { sessionId: entry.sessionId, modelId: req.modelId },
|
|
14474
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
14052
14475
|
});
|
|
14053
14476
|
} catch {
|
|
14054
14477
|
}
|
|
@@ -14078,16 +14501,17 @@ function createHttpAcpBridge(opts) {
|
|
|
14078
14501
|
} catch {
|
|
14079
14502
|
}
|
|
14080
14503
|
entry.events.close();
|
|
14081
|
-
if (ci && ci.sessionIds.size === 0) {
|
|
14504
|
+
if (ci && ci.sessionIds.size === 0 && ci.pendingRestoreIds.size === 0) {
|
|
14082
14505
|
ci.isDying = true;
|
|
14083
14506
|
await ci.channel.kill().catch(() => {
|
|
14084
14507
|
});
|
|
14085
14508
|
}
|
|
14086
14509
|
},
|
|
14087
|
-
async detachClient(sessionId) {
|
|
14510
|
+
async detachClient(sessionId, clientId) {
|
|
14088
14511
|
const entry = byId.get(sessionId);
|
|
14089
14512
|
if (!entry) return;
|
|
14090
14513
|
if (entry.attachCount > 0) entry.attachCount--;
|
|
14514
|
+
unregisterClient(entry, clientId);
|
|
14091
14515
|
if (entry.spawnOwnerWantedKill && entry.attachCount === 0 && entry.events.subscriberCount === 0) {
|
|
14092
14516
|
await this.killSession(sessionId).catch(() => {
|
|
14093
14517
|
});
|
|
@@ -14135,6 +14559,12 @@ function createHttpAcpBridge(opts) {
|
|
|
14135
14559
|
() => void 0
|
|
14136
14560
|
)
|
|
14137
14561
|
);
|
|
14562
|
+
const inFlightRestoreAwaits = Array.from(inFlightRestores.values()).map(
|
|
14563
|
+
(restore) => restore.promise.then(
|
|
14564
|
+
() => void 0,
|
|
14565
|
+
() => void 0
|
|
14566
|
+
)
|
|
14567
|
+
);
|
|
14138
14568
|
const inFlightChannelAwait = inFlightChannelSpawn ? inFlightChannelSpawn.then(
|
|
14139
14569
|
() => void 0,
|
|
14140
14570
|
() => void 0
|
|
@@ -14143,6 +14573,7 @@ function createHttpAcpBridge(opts) {
|
|
|
14143
14573
|
...channels.map((ci) => ci.channel.kill().catch(() => {
|
|
14144
14574
|
})),
|
|
14145
14575
|
...inFlightSessionAwaits,
|
|
14576
|
+
...inFlightRestoreAwaits,
|
|
14146
14577
|
inFlightChannelAwait
|
|
14147
14578
|
]);
|
|
14148
14579
|
}
|
|
@@ -14328,20 +14759,68 @@ function killChild(child) {
|
|
|
14328
14759
|
}
|
|
14329
14760
|
__name(killChild, "killChild");
|
|
14330
14761
|
|
|
14762
|
+
// packages/cli/src/serve/capabilities.ts
|
|
14763
|
+
init_esbuild_shims();
|
|
14764
|
+
var SERVE_PROTOCOL_VERSION = "v1";
|
|
14765
|
+
var SUPPORTED_SERVE_PROTOCOL_VERSIONS = [
|
|
14766
|
+
SERVE_PROTOCOL_VERSION
|
|
14767
|
+
];
|
|
14768
|
+
var SERVE_CAPABILITY_REGISTRY = {
|
|
14769
|
+
health: { since: "v1" },
|
|
14770
|
+
capabilities: { since: "v1" },
|
|
14771
|
+
session_create: { since: "v1" },
|
|
14772
|
+
session_scope_override: { since: "v1" },
|
|
14773
|
+
session_load: { since: "v1" },
|
|
14774
|
+
// ACP backs this with `connection.unstable_resumeSession`. Surface
|
|
14775
|
+
// the unstable prefix so clients don't pin against a `v1` shape that
|
|
14776
|
+
// the underlying ACP method may still change.
|
|
14777
|
+
unstable_session_resume: { since: "v1" },
|
|
14778
|
+
session_list: { since: "v1" },
|
|
14779
|
+
session_prompt: { since: "v1" },
|
|
14780
|
+
session_cancel: { since: "v1" },
|
|
14781
|
+
session_events: { since: "v1" },
|
|
14782
|
+
session_set_model: { since: "v1" },
|
|
14783
|
+
client_identity: { since: "v1" },
|
|
14784
|
+
session_permission_vote: { since: "v1" },
|
|
14785
|
+
permission_vote: { since: "v1" }
|
|
14786
|
+
};
|
|
14787
|
+
var SERVE_FEATURES = Object.freeze(
|
|
14788
|
+
Object.keys(SERVE_CAPABILITY_REGISTRY)
|
|
14789
|
+
);
|
|
14790
|
+
function serveProtocolVersionIndex(version) {
|
|
14791
|
+
return SUPPORTED_SERVE_PROTOCOL_VERSIONS.indexOf(version);
|
|
14792
|
+
}
|
|
14793
|
+
__name(serveProtocolVersionIndex, "serveProtocolVersionIndex");
|
|
14794
|
+
function isFeatureAvailableInProtocol(feature, protocolVersion) {
|
|
14795
|
+
return serveProtocolVersionIndex(SERVE_CAPABILITY_REGISTRY[feature].since) <= serveProtocolVersionIndex(protocolVersion);
|
|
14796
|
+
}
|
|
14797
|
+
__name(isFeatureAvailableInProtocol, "isFeatureAvailableInProtocol");
|
|
14798
|
+
function getRegisteredServeFeatures() {
|
|
14799
|
+
return [...SERVE_FEATURES];
|
|
14800
|
+
}
|
|
14801
|
+
__name(getRegisteredServeFeatures, "getRegisteredServeFeatures");
|
|
14802
|
+
function getAdvertisedServeFeatures(protocolVersion = SERVE_PROTOCOL_VERSION) {
|
|
14803
|
+
return SERVE_FEATURES.filter(
|
|
14804
|
+
(feature) => isFeatureAvailableInProtocol(feature, protocolVersion)
|
|
14805
|
+
);
|
|
14806
|
+
}
|
|
14807
|
+
__name(getAdvertisedServeFeatures, "getAdvertisedServeFeatures");
|
|
14808
|
+
function getServeFeatures() {
|
|
14809
|
+
return getAdvertisedServeFeatures();
|
|
14810
|
+
}
|
|
14811
|
+
__name(getServeFeatures, "getServeFeatures");
|
|
14812
|
+
function getServeProtocolVersions() {
|
|
14813
|
+
return {
|
|
14814
|
+
current: SERVE_PROTOCOL_VERSION,
|
|
14815
|
+
supported: [...SUPPORTED_SERVE_PROTOCOL_VERSIONS]
|
|
14816
|
+
};
|
|
14817
|
+
}
|
|
14818
|
+
__name(getServeProtocolVersions, "getServeProtocolVersions");
|
|
14819
|
+
|
|
14331
14820
|
// packages/cli/src/serve/types.ts
|
|
14332
14821
|
init_esbuild_shims();
|
|
14333
14822
|
var CAPABILITIES_SCHEMA_VERSION = 1;
|
|
14334
|
-
var STAGE1_FEATURES =
|
|
14335
|
-
"health",
|
|
14336
|
-
"capabilities",
|
|
14337
|
-
"session_create",
|
|
14338
|
-
"session_list",
|
|
14339
|
-
"session_prompt",
|
|
14340
|
-
"session_cancel",
|
|
14341
|
-
"session_events",
|
|
14342
|
-
"session_set_model",
|
|
14343
|
-
"permission_vote"
|
|
14344
|
-
];
|
|
14823
|
+
var STAGE1_FEATURES = SERVE_FEATURES;
|
|
14345
14824
|
|
|
14346
14825
|
// packages/cli/src/serve/server.ts
|
|
14347
14826
|
function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
@@ -14385,8 +14864,9 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
14385
14864
|
app.get("/capabilities", (_req, res) => {
|
|
14386
14865
|
const envelope = {
|
|
14387
14866
|
v: CAPABILITIES_SCHEMA_VERSION,
|
|
14867
|
+
protocolVersions: getServeProtocolVersions(),
|
|
14388
14868
|
mode: opts.mode,
|
|
14389
|
-
features:
|
|
14869
|
+
features: getAdvertisedServeFeatures(),
|
|
14390
14870
|
modelServices: [],
|
|
14391
14871
|
// #3803 §02: surface the bound workspace so clients can detect
|
|
14392
14872
|
// mismatch pre-flight and omit `cwd` on `POST /session`.
|
|
@@ -14413,17 +14893,33 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
14413
14893
|
return;
|
|
14414
14894
|
}
|
|
14415
14895
|
const modelServiceId = typeof body["modelServiceId"] === "string" ? body["modelServiceId"] : void 0;
|
|
14896
|
+
const rawSessionScope = body["sessionScope"];
|
|
14897
|
+
let sessionScope;
|
|
14898
|
+
if (rawSessionScope !== void 0) {
|
|
14899
|
+
if (rawSessionScope !== "single" && rawSessionScope !== "thread") {
|
|
14900
|
+
res.status(400).json({
|
|
14901
|
+
error: '`sessionScope` must be "single" or "thread" when provided',
|
|
14902
|
+
code: "invalid_session_scope"
|
|
14903
|
+
});
|
|
14904
|
+
return;
|
|
14905
|
+
}
|
|
14906
|
+
sessionScope = rawSessionScope;
|
|
14907
|
+
}
|
|
14908
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14909
|
+
if (clientId === null) return;
|
|
14416
14910
|
try {
|
|
14417
14911
|
const session = await bridge.spawnOrAttach({
|
|
14418
14912
|
workspaceCwd: cwd,
|
|
14419
|
-
modelServiceId
|
|
14913
|
+
modelServiceId,
|
|
14914
|
+
...clientId !== void 0 ? { clientId } : {},
|
|
14915
|
+
...sessionScope !== void 0 ? { sessionScope } : {}
|
|
14420
14916
|
});
|
|
14421
14917
|
if (!res.writable) {
|
|
14422
14918
|
if (!session.attached) {
|
|
14423
14919
|
bridge.killSession(session.sessionId, { requireZeroAttaches: true }).catch(() => {
|
|
14424
14920
|
});
|
|
14425
14921
|
} else {
|
|
14426
|
-
bridge.detachClient(session.sessionId).catch(() => {
|
|
14922
|
+
bridge.detachClient(session.sessionId, session.clientId).catch(() => {
|
|
14427
14923
|
});
|
|
14428
14924
|
}
|
|
14429
14925
|
return;
|
|
@@ -14433,6 +14929,47 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
14433
14929
|
sendBridgeError(res, err, { route: "POST /session" });
|
|
14434
14930
|
}
|
|
14435
14931
|
});
|
|
14932
|
+
const restoreSessionHandler = /* @__PURE__ */ __name((action) => async (req, res) => {
|
|
14933
|
+
const sessionId = req.params["id"];
|
|
14934
|
+
if (!sessionId) {
|
|
14935
|
+
res.status(400).json({ error: "`sessionId` route parameter is required" });
|
|
14936
|
+
return;
|
|
14937
|
+
}
|
|
14938
|
+
const body = safeBody(req);
|
|
14939
|
+
const cwd = parseOptionalWorkspaceCwd(body, boundWorkspace, res);
|
|
14940
|
+
if (cwd === void 0) return;
|
|
14941
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14942
|
+
if (clientId === null) return;
|
|
14943
|
+
try {
|
|
14944
|
+
const session = action === "load" ? await bridge.loadSession({
|
|
14945
|
+
sessionId,
|
|
14946
|
+
workspaceCwd: cwd,
|
|
14947
|
+
...clientId !== void 0 ? { clientId } : {}
|
|
14948
|
+
}) : await bridge.resumeSession({
|
|
14949
|
+
sessionId,
|
|
14950
|
+
workspaceCwd: cwd,
|
|
14951
|
+
...clientId !== void 0 ? { clientId } : {}
|
|
14952
|
+
});
|
|
14953
|
+
if (!res.writable) {
|
|
14954
|
+
if (!session.attached) {
|
|
14955
|
+
bridge.killSession(session.sessionId, { requireZeroAttaches: true }).catch(() => {
|
|
14956
|
+
});
|
|
14957
|
+
} else {
|
|
14958
|
+
bridge.detachClient(session.sessionId, session.clientId).catch(() => {
|
|
14959
|
+
});
|
|
14960
|
+
}
|
|
14961
|
+
return;
|
|
14962
|
+
}
|
|
14963
|
+
res.status(200).json(session);
|
|
14964
|
+
} catch (err) {
|
|
14965
|
+
sendBridgeError(res, err, {
|
|
14966
|
+
route: `POST /session/:id/${action}`,
|
|
14967
|
+
sessionId
|
|
14968
|
+
});
|
|
14969
|
+
}
|
|
14970
|
+
}, "restoreSessionHandler");
|
|
14971
|
+
app.post("/session/:id/load", restoreSessionHandler("load"));
|
|
14972
|
+
app.post("/session/:id/resume", restoreSessionHandler("resume"));
|
|
14436
14973
|
app.post("/session/:id/prompt", async (req, res) => {
|
|
14437
14974
|
const sessionId = req.params["id"];
|
|
14438
14975
|
const body = safeBody(req);
|
|
@@ -14464,6 +15001,11 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
14464
15001
|
if (!res.writableEnded) abort.abort();
|
|
14465
15002
|
}, "onResClose");
|
|
14466
15003
|
res.once("close", onResClose);
|
|
15004
|
+
const clientId = parseClientIdHeader(req, res);
|
|
15005
|
+
if (clientId === null) {
|
|
15006
|
+
res.off("close", onResClose);
|
|
15007
|
+
return;
|
|
15008
|
+
}
|
|
14467
15009
|
try {
|
|
14468
15010
|
const result = await bridge.sendPrompt(
|
|
14469
15011
|
sessionId,
|
|
@@ -14472,7 +15014,8 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
14472
15014
|
sessionId,
|
|
14473
15015
|
prompt
|
|
14474
15016
|
},
|
|
14475
|
-
abort.signal
|
|
15017
|
+
abort.signal,
|
|
15018
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
14476
15019
|
);
|
|
14477
15020
|
res.status(200).json(result);
|
|
14478
15021
|
} catch (err) {
|
|
@@ -14490,11 +15033,17 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
14490
15033
|
app.post("/session/:id/cancel", async (req, res) => {
|
|
14491
15034
|
const sessionId = req.params["id"];
|
|
14492
15035
|
const body = safeBody(req);
|
|
15036
|
+
const clientId = parseClientIdHeader(req, res);
|
|
15037
|
+
if (clientId === null) return;
|
|
14493
15038
|
try {
|
|
14494
|
-
await bridge.cancelSession(
|
|
14495
|
-
|
|
14496
|
-
|
|
14497
|
-
|
|
15039
|
+
await bridge.cancelSession(
|
|
15040
|
+
sessionId,
|
|
15041
|
+
{
|
|
15042
|
+
...body,
|
|
15043
|
+
sessionId
|
|
15044
|
+
},
|
|
15045
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
15046
|
+
);
|
|
14498
15047
|
res.status(204).end();
|
|
14499
15048
|
} catch (err) {
|
|
14500
15049
|
sendBridgeError(res, err, {
|
|
@@ -14532,12 +15081,18 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
14532
15081
|
});
|
|
14533
15082
|
return;
|
|
14534
15083
|
}
|
|
15084
|
+
const clientId = parseClientIdHeader(req, res);
|
|
15085
|
+
if (clientId === null) return;
|
|
14535
15086
|
try {
|
|
14536
|
-
const response = await bridge.setSessionModel(
|
|
14537
|
-
...body,
|
|
15087
|
+
const response = await bridge.setSessionModel(
|
|
14538
15088
|
sessionId,
|
|
14539
|
-
|
|
14540
|
-
|
|
15089
|
+
{
|
|
15090
|
+
...body,
|
|
15091
|
+
sessionId,
|
|
15092
|
+
modelId
|
|
15093
|
+
},
|
|
15094
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
15095
|
+
);
|
|
14541
15096
|
res.status(200).json(response);
|
|
14542
15097
|
} catch (err) {
|
|
14543
15098
|
sendBridgeError(res, err, {
|
|
@@ -14546,33 +15101,56 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
14546
15101
|
});
|
|
14547
15102
|
}
|
|
14548
15103
|
});
|
|
14549
|
-
app.post("/permission/:requestId", (req, res) => {
|
|
15104
|
+
app.post("/session/:id/permission/:requestId", (req, res) => {
|
|
15105
|
+
const sessionId = req.params["id"];
|
|
14550
15106
|
const requestId = req.params["requestId"];
|
|
14551
|
-
const
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
|
|
14555
|
-
|
|
15107
|
+
const response = parsePermissionVoteBody(req, res);
|
|
15108
|
+
if (response === void 0) return;
|
|
15109
|
+
const clientId = parseClientIdHeader(req, res);
|
|
15110
|
+
if (clientId === null) return;
|
|
15111
|
+
let accepted;
|
|
15112
|
+
try {
|
|
15113
|
+
accepted = bridge.respondToSessionPermission(
|
|
15114
|
+
sessionId,
|
|
15115
|
+
requestId,
|
|
15116
|
+
response,
|
|
15117
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
15118
|
+
);
|
|
15119
|
+
} catch (err) {
|
|
15120
|
+
sendPermissionVoteError(res, err, {
|
|
15121
|
+
route: "POST /session/:id/permission/:requestId",
|
|
15122
|
+
sessionId
|
|
15123
|
+
});
|
|
15124
|
+
return;
|
|
15125
|
+
}
|
|
15126
|
+
if (!accepted) {
|
|
15127
|
+
res.status(404).json({
|
|
15128
|
+
error: "No pending permission request for session",
|
|
15129
|
+
sessionId,
|
|
15130
|
+
requestId
|
|
14556
15131
|
});
|
|
14557
15132
|
return;
|
|
14558
15133
|
}
|
|
15134
|
+
res.status(200).json({});
|
|
15135
|
+
});
|
|
15136
|
+
app.post("/permission/:requestId", (req, res) => {
|
|
15137
|
+
const requestId = req.params["requestId"];
|
|
15138
|
+
const response = parsePermissionVoteBody(req, res);
|
|
15139
|
+
if (response === void 0) return;
|
|
15140
|
+
const clientId = parseClientIdHeader(req, res);
|
|
15141
|
+
if (clientId === null) return;
|
|
14559
15142
|
let accepted;
|
|
14560
15143
|
try {
|
|
14561
|
-
accepted = bridge.respondToPermission(
|
|
14562
|
-
|
|
14563
|
-
|
|
14564
|
-
|
|
15144
|
+
accepted = bridge.respondToPermission(
|
|
15145
|
+
requestId,
|
|
15146
|
+
response,
|
|
15147
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
15148
|
+
);
|
|
14565
15149
|
} catch (err) {
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
|
|
14569
|
-
|
|
14570
|
-
requestId: err.requestId,
|
|
14571
|
-
optionId: err.optionId
|
|
14572
|
-
});
|
|
14573
|
-
return;
|
|
14574
|
-
}
|
|
14575
|
-
throw err;
|
|
15150
|
+
sendPermissionVoteError(res, err, {
|
|
15151
|
+
route: "POST /permission/:requestId"
|
|
15152
|
+
});
|
|
15153
|
+
return;
|
|
14576
15154
|
}
|
|
14577
15155
|
if (!accepted) {
|
|
14578
15156
|
res.status(404).json({ error: "No pending permission request", requestId });
|
|
@@ -14728,6 +15306,10 @@ var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
14728
15306
|
"constructor",
|
|
14729
15307
|
"prototype"
|
|
14730
15308
|
]);
|
|
15309
|
+
var CLIENT_ID_HEADER = "x-qwen-client-id";
|
|
15310
|
+
var MAX_CLIENT_ID_LENGTH = 128;
|
|
15311
|
+
var CLIENT_ID_RE = /^[A-Za-z0-9._:-]+$/;
|
|
15312
|
+
var INVALID_PERMISSION_OUTCOME_ERROR = '`outcome` must be `{ outcome: "cancelled" }` or `{ outcome: "selected", optionId: string }`';
|
|
14731
15313
|
function safeBody(req) {
|
|
14732
15314
|
const raw = req.body;
|
|
14733
15315
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
@@ -14741,6 +15323,52 @@ function safeBody(req) {
|
|
|
14741
15323
|
return out;
|
|
14742
15324
|
}
|
|
14743
15325
|
__name(safeBody, "safeBody");
|
|
15326
|
+
function parseOptionalWorkspaceCwd(body, boundWorkspace, res) {
|
|
15327
|
+
const hasCwd = "cwd" in body;
|
|
15328
|
+
if (hasCwd && typeof body["cwd"] !== "string") {
|
|
15329
|
+
res.status(400).json({ error: "`cwd` must be a string absolute path when provided" });
|
|
15330
|
+
return void 0;
|
|
15331
|
+
}
|
|
15332
|
+
if (hasCwd && body["cwd"].length > MAX_WORKSPACE_PATH_LENGTH) {
|
|
15333
|
+
res.status(400).json({
|
|
15334
|
+
error: `\`cwd\` exceeds the ${MAX_WORKSPACE_PATH_LENGTH}-character limit`
|
|
15335
|
+
});
|
|
15336
|
+
return void 0;
|
|
15337
|
+
}
|
|
15338
|
+
const cwd = hasCwd ? body["cwd"] : boundWorkspace;
|
|
15339
|
+
if (!path2.isAbsolute(cwd)) {
|
|
15340
|
+
res.status(400).json({ error: "`cwd` must be an absolute path when provided" });
|
|
15341
|
+
return void 0;
|
|
15342
|
+
}
|
|
15343
|
+
return cwd;
|
|
15344
|
+
}
|
|
15345
|
+
__name(parseOptionalWorkspaceCwd, "parseOptionalWorkspaceCwd");
|
|
15346
|
+
function parseClientIdHeader(req, res) {
|
|
15347
|
+
const raw = req.get(CLIENT_ID_HEADER);
|
|
15348
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
15349
|
+
if (raw.length > MAX_CLIENT_ID_LENGTH || !CLIENT_ID_RE.test(raw)) {
|
|
15350
|
+
res.status(400).json({
|
|
15351
|
+
error: "`X-Qwen-Client-Id` must be a non-empty token of 128 characters or fewer",
|
|
15352
|
+
code: "invalid_client_id"
|
|
15353
|
+
});
|
|
15354
|
+
return null;
|
|
15355
|
+
}
|
|
15356
|
+
return raw;
|
|
15357
|
+
}
|
|
15358
|
+
__name(parseClientIdHeader, "parseClientIdHeader");
|
|
15359
|
+
function parsePermissionVoteBody(req, res) {
|
|
15360
|
+
const body = safeBody(req);
|
|
15361
|
+
const outcome = body["outcome"];
|
|
15362
|
+
if (!isValidOutcome(outcome)) {
|
|
15363
|
+
res.status(400).json({ error: INVALID_PERMISSION_OUTCOME_ERROR });
|
|
15364
|
+
return void 0;
|
|
15365
|
+
}
|
|
15366
|
+
return {
|
|
15367
|
+
...body,
|
|
15368
|
+
outcome
|
|
15369
|
+
};
|
|
15370
|
+
}
|
|
15371
|
+
__name(parsePermissionVoteBody, "parsePermissionVoteBody");
|
|
14744
15372
|
function isValidOutcome(raw) {
|
|
14745
15373
|
if (typeof raw !== "object" || raw === null) return false;
|
|
14746
15374
|
const obj = raw;
|
|
@@ -14767,6 +15395,19 @@ function parseLastEventId(raw) {
|
|
|
14767
15395
|
return n;
|
|
14768
15396
|
}
|
|
14769
15397
|
__name(parseLastEventId, "parseLastEventId");
|
|
15398
|
+
function sendPermissionVoteError(res, err, ctx) {
|
|
15399
|
+
if (err instanceof InvalidPermissionOptionError) {
|
|
15400
|
+
res.status(400).json({
|
|
15401
|
+
error: err.message,
|
|
15402
|
+
code: "invalid_option_id",
|
|
15403
|
+
requestId: err.requestId,
|
|
15404
|
+
optionId: err.optionId
|
|
15405
|
+
});
|
|
15406
|
+
return;
|
|
15407
|
+
}
|
|
15408
|
+
sendBridgeError(res, err, ctx);
|
|
15409
|
+
}
|
|
15410
|
+
__name(sendPermissionVoteError, "sendPermissionVoteError");
|
|
14770
15411
|
function formatSseFrame(event) {
|
|
14771
15412
|
const dataJson = JSON.stringify(event);
|
|
14772
15413
|
const idLine = "id" in event && event.id !== void 0 ? `id: ${event.id}
|
|
@@ -14782,6 +15423,15 @@ function sendBridgeError(res, err, ctx) {
|
|
|
14782
15423
|
res.status(404).json({ error: err.message, sessionId: err.sessionId });
|
|
14783
15424
|
return;
|
|
14784
15425
|
}
|
|
15426
|
+
if (err instanceof InvalidClientIdError) {
|
|
15427
|
+
res.status(400).json({
|
|
15428
|
+
error: err.message,
|
|
15429
|
+
code: "invalid_client_id",
|
|
15430
|
+
sessionId: err.sessionId,
|
|
15431
|
+
clientId: err.clientId
|
|
15432
|
+
});
|
|
15433
|
+
return;
|
|
15434
|
+
}
|
|
14785
15435
|
if (err instanceof WorkspaceMismatchError) {
|
|
14786
15436
|
writeStderrLine(
|
|
14787
15437
|
`qwen serve: workspace_mismatch (POST /session): daemon bound to ${JSON.stringify(err.bound)}, rejected ${JSON.stringify(err.requested)}`
|
|
@@ -14794,6 +15444,13 @@ function sendBridgeError(res, err, ctx) {
|
|
|
14794
15444
|
});
|
|
14795
15445
|
return;
|
|
14796
15446
|
}
|
|
15447
|
+
if (err instanceof InvalidSessionScopeError) {
|
|
15448
|
+
res.status(400).json({
|
|
15449
|
+
error: err.message,
|
|
15450
|
+
code: "invalid_session_scope"
|
|
15451
|
+
});
|
|
15452
|
+
return;
|
|
15453
|
+
}
|
|
14797
15454
|
if (err instanceof SessionLimitExceededError) {
|
|
14798
15455
|
res.set("Retry-After", "5");
|
|
14799
15456
|
res.status(503).json({
|
|
@@ -14803,6 +15460,17 @@ function sendBridgeError(res, err, ctx) {
|
|
|
14803
15460
|
});
|
|
14804
15461
|
return;
|
|
14805
15462
|
}
|
|
15463
|
+
if (err instanceof RestoreInProgressError) {
|
|
15464
|
+
res.set("Retry-After", "5");
|
|
15465
|
+
res.status(409).json({
|
|
15466
|
+
error: err.message,
|
|
15467
|
+
code: "restore_in_progress",
|
|
15468
|
+
sessionId: err.sessionId,
|
|
15469
|
+
activeAction: err.activeAction,
|
|
15470
|
+
requestedAction: err.requestedAction
|
|
15471
|
+
});
|
|
15472
|
+
return;
|
|
15473
|
+
}
|
|
14806
15474
|
const ctxParts = [
|
|
14807
15475
|
ctx?.route,
|
|
14808
15476
|
ctx?.sessionId ? `session=${ctx.sessionId}` : void 0
|
|
@@ -15066,12 +15734,20 @@ export {
|
|
|
15066
15734
|
CAPABILITIES_SCHEMA_VERSION,
|
|
15067
15735
|
EVENT_SCHEMA_VERSION,
|
|
15068
15736
|
EventBus,
|
|
15737
|
+
SERVE_CAPABILITY_REGISTRY,
|
|
15738
|
+
SERVE_FEATURES,
|
|
15739
|
+
SERVE_PROTOCOL_VERSION,
|
|
15069
15740
|
STAGE1_FEATURES,
|
|
15741
|
+
SUPPORTED_SERVE_PROTOCOL_VERSIONS,
|
|
15070
15742
|
SessionNotFoundError,
|
|
15071
15743
|
createHttpAcpBridge,
|
|
15072
15744
|
createInMemoryChannel,
|
|
15073
15745
|
createServeApp,
|
|
15074
15746
|
defaultSpawnChannelFactory,
|
|
15747
|
+
getAdvertisedServeFeatures,
|
|
15748
|
+
getRegisteredServeFeatures,
|
|
15749
|
+
getServeFeatures,
|
|
15750
|
+
getServeProtocolVersions,
|
|
15075
15751
|
runQwenServe
|
|
15076
15752
|
};
|
|
15077
15753
|
/**
|