@linzumi/cli 0.0.36-beta → 0.0.37-beta
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 +1 -1
- package/dist/index.js +112 -38
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1481,7 +1481,7 @@ var maxForwardedTurnIds = 64;
|
|
|
1481
1481
|
async function attachChannelSession(args) {
|
|
1482
1482
|
const session = args.options.channelSession;
|
|
1483
1483
|
const chatTopic = `chat:${session.workspaceSlug}:${session.channelSlug}`;
|
|
1484
|
-
const state = initialChannelSessionState(0, session.kandanThreadId, args.options);
|
|
1484
|
+
const state = initialChannelSessionState(0, session.rootSeq, session.kandanThreadId, session.codexThreadId, args.options);
|
|
1485
1485
|
const joined = await args.kandan.join(chatTopic, { last_seq: 0 }, {
|
|
1486
1486
|
rejoinPayload: () => ({ last_seq: state.minSeq })
|
|
1487
1487
|
});
|
|
@@ -1599,6 +1599,7 @@ async function attachChannelSession(args) {
|
|
|
1599
1599
|
}
|
|
1600
1600
|
},
|
|
1601
1601
|
handleControl: (control) => handleChannelSessionControl(args, state, payloadContext, control),
|
|
1602
|
+
startThreadMessageTurn: (message) => startThreadMessageTurn(args, state, payloadContext, message),
|
|
1602
1603
|
currentRuntimeSettings: () => state.runtimeSettings,
|
|
1603
1604
|
currentCodexThreadId: () => state.codexThreadId,
|
|
1604
1605
|
currentKandanThreadId: () => state.kandanThreadId,
|
|
@@ -1641,11 +1642,11 @@ async function bindCurrentCodexThread(args, state) {
|
|
|
1641
1642
|
instance_id: args.instanceId
|
|
1642
1643
|
});
|
|
1643
1644
|
}
|
|
1644
|
-
function initialChannelSessionState(cursor, kandanThreadId, options) {
|
|
1645
|
+
function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThreadId, options) {
|
|
1645
1646
|
return {
|
|
1646
|
-
rootSeq
|
|
1647
|
+
rootSeq,
|
|
1647
1648
|
kandanThreadId,
|
|
1648
|
-
codexThreadId
|
|
1649
|
+
codexThreadId,
|
|
1649
1650
|
turn: { status: "idle" },
|
|
1650
1651
|
closed: false,
|
|
1651
1652
|
minSeq: cursor,
|
|
@@ -1712,6 +1713,15 @@ async function bindChannelSession(args, state, payloadContext) {
|
|
|
1712
1713
|
if (state.rootSeq !== undefined) {
|
|
1713
1714
|
state.minSeq = Math.max(state.minSeq, state.rootSeq);
|
|
1714
1715
|
}
|
|
1716
|
+
} else if (state.codexThreadId !== undefined) {
|
|
1717
|
+
await bindCurrentCodexThread(args, state);
|
|
1718
|
+
switch (state.rootSeq) {
|
|
1719
|
+
case undefined:
|
|
1720
|
+
await postBoundThreadAvailability(args, state, payloadContext, codexVersion);
|
|
1721
|
+
break;
|
|
1722
|
+
default:
|
|
1723
|
+
break;
|
|
1724
|
+
}
|
|
1715
1725
|
} else {
|
|
1716
1726
|
const resolved = await pushOk(args.kandan, args.topic, "session:resolve_thread_session", {
|
|
1717
1727
|
workspace: session.workspaceSlug,
|
|
@@ -1723,15 +1733,22 @@ async function bindChannelSession(args, state, payloadContext) {
|
|
|
1723
1733
|
if (state.codexThreadId === undefined) {
|
|
1724
1734
|
throw new Error("Kandan thread root metadata did not include a Codex thread id");
|
|
1725
1735
|
}
|
|
1726
|
-
await
|
|
1727
|
-
workspace: session.workspaceSlug,
|
|
1728
|
-
channel: session.channelSlug,
|
|
1729
|
-
thread_id: state.kandanThreadId,
|
|
1730
|
-
body: availabilityMessage(args.options, codexVersion, state.codexThreadId),
|
|
1731
|
-
payload: localRunnerPayload(args.options, args.instanceId, "availability", state.codexThreadId, payloadContext)
|
|
1732
|
-
});
|
|
1736
|
+
await postBoundThreadAvailability(args, state, payloadContext, codexVersion);
|
|
1733
1737
|
}
|
|
1734
1738
|
}
|
|
1739
|
+
async function postBoundThreadAvailability(args, state, payloadContext, codexVersion) {
|
|
1740
|
+
if (state.kandanThreadId === undefined || state.codexThreadId === undefined) {
|
|
1741
|
+
throw new Error("cannot post local Codex availability before thread binding");
|
|
1742
|
+
}
|
|
1743
|
+
const session = args.options.channelSession;
|
|
1744
|
+
await pushOk(args.kandan, args.topic, "session:post_thread_message", {
|
|
1745
|
+
workspace: session.workspaceSlug,
|
|
1746
|
+
channel: session.channelSlug,
|
|
1747
|
+
thread_id: state.kandanThreadId,
|
|
1748
|
+
body: availabilityMessage(args.options, codexVersion, state.codexThreadId),
|
|
1749
|
+
payload: localRunnerPayload(args.options, args.instanceId, "availability", state.codexThreadId, payloadContext)
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1735
1752
|
async function handleChannelSessionControl(args, state, payloadContext, control) {
|
|
1736
1753
|
if (control.type === "update_session_settings") {
|
|
1737
1754
|
return updateSessionSettings(args, state, control);
|
|
@@ -1745,7 +1762,10 @@ async function handleChannelSessionControl(args, state, payloadContext, control)
|
|
|
1745
1762
|
if (control.type !== "interrupt_queued_messages") {
|
|
1746
1763
|
return;
|
|
1747
1764
|
}
|
|
1748
|
-
if (state.codexThreadId
|
|
1765
|
+
if (state.codexThreadId !== control.threadId) {
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
if (state.codexThreadId === undefined || state.kandanThreadId === undefined) {
|
|
1749
1769
|
return { instanceId: args.instanceId, ok: false, error: "thread_not_bound" };
|
|
1750
1770
|
}
|
|
1751
1771
|
const interrupted = interruptPendingKandanMessages(state.queue, control.throughSeq);
|
|
@@ -1795,7 +1815,10 @@ async function handleChannelSessionControl(args, state, payloadContext, control)
|
|
|
1795
1815
|
};
|
|
1796
1816
|
}
|
|
1797
1817
|
function updateSessionSettings(args, state, control) {
|
|
1798
|
-
if (state.codexThreadId
|
|
1818
|
+
if (state.codexThreadId !== control.threadId) {
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
if (state.codexThreadId === undefined) {
|
|
1799
1822
|
return { instanceId: args.instanceId, ok: false, error: "thread_not_bound" };
|
|
1800
1823
|
}
|
|
1801
1824
|
state.runtimeSettings = mergeRuntimeSettings(state.runtimeSettings, control);
|
|
@@ -1822,7 +1845,10 @@ function updateSessionSettings(args, state, control) {
|
|
|
1822
1845
|
};
|
|
1823
1846
|
}
|
|
1824
1847
|
async function resolvePendingCodexApprovalRequest(args, state, control) {
|
|
1825
|
-
if (state.codexThreadId
|
|
1848
|
+
if (state.codexThreadId !== control.threadId) {
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
if (state.codexThreadId === undefined || state.kandanThreadId === undefined) {
|
|
1826
1852
|
return { instanceId: args.instanceId, ok: false, error: "thread_not_bound" };
|
|
1827
1853
|
}
|
|
1828
1854
|
const approval = state.pendingApprovalRequests.get(approvalRequestKey(control.requestId, control.sourceSeq));
|
|
@@ -1836,7 +1862,7 @@ async function resolvePendingCodexApprovalRequest(args, state, control) {
|
|
|
1836
1862
|
await publishMessageState(args, state.kandanThreadId, approval.sourceSeq, {
|
|
1837
1863
|
status: "processing",
|
|
1838
1864
|
reason: "streaming response"
|
|
1839
|
-
});
|
|
1865
|
+
}, undefined, undefined, state.codexThreadId);
|
|
1840
1866
|
args.log("codex.approval_request_resolved", {
|
|
1841
1867
|
request_id: control.requestId,
|
|
1842
1868
|
source_seq: control.sourceSeq,
|
|
@@ -1846,6 +1872,10 @@ async function resolvePendingCodexApprovalRequest(args, state, control) {
|
|
|
1846
1872
|
return { instanceId: args.instanceId, ok: true };
|
|
1847
1873
|
}
|
|
1848
1874
|
async function resolvePendingPortForwardRequest(args, state, payloadContext, control) {
|
|
1875
|
+
const request = state.pendingPortForwardRequests.get(control.requestId);
|
|
1876
|
+
if (request === undefined) {
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1849
1879
|
if (!portForwardControlSenderAllowed(args, payloadContext, control)) {
|
|
1850
1880
|
args.log("port_forward.request_resolution_ignored", {
|
|
1851
1881
|
request_id: control.requestId,
|
|
@@ -1855,10 +1885,6 @@ async function resolvePendingPortForwardRequest(args, state, payloadContext, con
|
|
|
1855
1885
|
});
|
|
1856
1886
|
return { instanceId: args.instanceId, ok: false, error: "sender_not_allowed" };
|
|
1857
1887
|
}
|
|
1858
|
-
const request = state.pendingPortForwardRequests.get(control.requestId);
|
|
1859
|
-
if (request === undefined) {
|
|
1860
|
-
return { instanceId: args.instanceId, ok: false, error: "port_forward_request_not_found" };
|
|
1861
|
-
}
|
|
1862
1888
|
state.pendingPortForwardRequests.delete(control.requestId);
|
|
1863
1889
|
if (control.decision === "deny") {
|
|
1864
1890
|
state.dismissedForwardTargets.set(request.port, approvedTargetFromRequest(request));
|
|
@@ -2204,6 +2230,27 @@ async function handleKandanChatEvent(args, state, runnerIdentity, payloadContext
|
|
|
2204
2230
|
await publishKandanMessageState(args, event, { status: "queued" });
|
|
2205
2231
|
await drainKandanMessageQueue(args, state, payloadContext);
|
|
2206
2232
|
}
|
|
2233
|
+
async function startThreadMessageTurn(args, state, payloadContext, message) {
|
|
2234
|
+
if (state.kandanThreadId === undefined || state.codexThreadId === undefined) {
|
|
2235
|
+
throw new Error("cannot start a local Codex turn before thread binding");
|
|
2236
|
+
}
|
|
2237
|
+
const queued = {
|
|
2238
|
+
seq: message.seq,
|
|
2239
|
+
actorSlug: message.actorSlug,
|
|
2240
|
+
actorUserId: message.actorUserId,
|
|
2241
|
+
body: message.body,
|
|
2242
|
+
attachments: []
|
|
2243
|
+
};
|
|
2244
|
+
enqueuePendingKandanMessage(state.queue, queued);
|
|
2245
|
+
args.log("kandan.message_queued", {
|
|
2246
|
+
seq: queued.seq,
|
|
2247
|
+
actor_slug: queued.actorSlug ?? null,
|
|
2248
|
+
actor_user_id: queued.actorUserId ?? null,
|
|
2249
|
+
queue_depth: pendingKandanMessageQueueLength(state.queue)
|
|
2250
|
+
});
|
|
2251
|
+
await publishQueuedMessageState(args, state, queued, { status: "queued" });
|
|
2252
|
+
await drainKandanMessageQueue(args, state, payloadContext);
|
|
2253
|
+
}
|
|
2207
2254
|
async function bindUnboundHistoricalThread(args, state, event) {
|
|
2208
2255
|
if (event.threadId === undefined || event.replyToSeq === undefined) {
|
|
2209
2256
|
return false;
|
|
@@ -2362,7 +2409,11 @@ async function drainKandanMessageQueue(args, state, payloadContext) {
|
|
|
2362
2409
|
}
|
|
2363
2410
|
async function handleCodexServerRequest(args, state, payloadContext, request) {
|
|
2364
2411
|
const params = objectValue(request.params) ?? {};
|
|
2365
|
-
const turnId =
|
|
2412
|
+
const turnId = codexNotificationTurnId(params);
|
|
2413
|
+
const threadId = codexNotificationThreadId(params);
|
|
2414
|
+
if (state.closed || !codexNotificationBelongsToSession(state, threadId, turnId)) {
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2366
2417
|
if (codexApprovalRequestCanAutoAccept(state.runtimeSettings, request.method)) {
|
|
2367
2418
|
args.log("codex.server_request_auto_accepted", {
|
|
2368
2419
|
method: request.method,
|
|
@@ -2405,7 +2456,7 @@ async function requestKandanApproval(args, state, request, turnId, payloadContex
|
|
|
2405
2456
|
status: "processing",
|
|
2406
2457
|
reason: "awaiting approval",
|
|
2407
2458
|
approval
|
|
2408
|
-
});
|
|
2459
|
+
}, undefined, undefined, state.codexThreadId);
|
|
2409
2460
|
args.log("codex.approval_request_pending", {
|
|
2410
2461
|
request_id: approval.requestId,
|
|
2411
2462
|
source_seq: sourceSeq,
|
|
@@ -3576,12 +3627,13 @@ async function publishQueuedMessageState(args, state, message, messageState) {
|
|
|
3576
3627
|
}
|
|
3577
3628
|
await publishMessageState(args, state.kandanThreadId, message.seq, messageState, message.actorSlug, message.actorUserId);
|
|
3578
3629
|
}
|
|
3579
|
-
async function publishMessageState(args, threadId, seq, state, actorSlug, actorUserId) {
|
|
3630
|
+
async function publishMessageState(args, threadId, seq, state, actorSlug, actorUserId, codexThreadId) {
|
|
3580
3631
|
const session = args.options.channelSession;
|
|
3581
3632
|
const payload = {
|
|
3582
3633
|
workspace: session.workspaceSlug,
|
|
3583
3634
|
channel: session.channelSlug,
|
|
3584
3635
|
thread_id: threadId,
|
|
3636
|
+
...codexThreadId === undefined ? {} : { codex_thread_id: codexThreadId },
|
|
3585
3637
|
seq,
|
|
3586
3638
|
status: state.status,
|
|
3587
3639
|
..."reason" in state ? { reason: state.reason } : {},
|
|
@@ -3639,7 +3691,7 @@ async function refreshActiveProcessingHeartbeat(args, state) {
|
|
|
3639
3691
|
if (activeProcessingState === undefined || state.kandanThreadId === undefined) {
|
|
3640
3692
|
return;
|
|
3641
3693
|
}
|
|
3642
|
-
await publishMessageState(args, state.kandanThreadId, activeProcessingState.seq, processingMessageStateFromActive(activeProcessingState));
|
|
3694
|
+
await publishMessageState(args, state.kandanThreadId, activeProcessingState.seq, processingMessageStateFromActive(activeProcessingState), undefined, undefined, state.codexThreadId);
|
|
3643
3695
|
}
|
|
3644
3696
|
function clearActiveProcessingState(state, seq) {
|
|
3645
3697
|
if (state.activeProcessingState?.seq === seq) {
|
|
@@ -4305,12 +4357,21 @@ async function respondToServerRequest(websocket, request, callbacks) {
|
|
|
4305
4357
|
}));
|
|
4306
4358
|
return;
|
|
4307
4359
|
}
|
|
4308
|
-
const callback
|
|
4309
|
-
|
|
4310
|
-
|
|
4360
|
+
for (const callback of callbacks) {
|
|
4361
|
+
const result = await callback(request);
|
|
4362
|
+
if (result !== undefined) {
|
|
4363
|
+
websocket.send(JSON.stringify({ jsonrpc: "2.0", id: request.id, result }));
|
|
4364
|
+
return;
|
|
4365
|
+
}
|
|
4311
4366
|
}
|
|
4312
|
-
|
|
4313
|
-
|
|
4367
|
+
websocket.send(JSON.stringify({
|
|
4368
|
+
jsonrpc: "2.0",
|
|
4369
|
+
id: request.id,
|
|
4370
|
+
error: {
|
|
4371
|
+
code: -32601,
|
|
4372
|
+
message: `unhandled Codex app-server request: ${request.method}`
|
|
4373
|
+
}
|
|
4374
|
+
}));
|
|
4314
4375
|
} catch (error) {
|
|
4315
4376
|
websocket.send(JSON.stringify({
|
|
4316
4377
|
jsonrpc: "2.0",
|
|
@@ -6981,7 +7042,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
6981
7042
|
await Promise.all(Array.from(dynamicChannelSessions.values(), (session) => session.close()));
|
|
6982
7043
|
dynamicChannelSessions.clear();
|
|
6983
7044
|
});
|
|
6984
|
-
const attachStartedThreadSession = async (control, cwd) => {
|
|
7045
|
+
const attachStartedThreadSession = async (control, cwd, codexThreadId) => {
|
|
6985
7046
|
const workspaceSlug = normalizedWorkDescription(control.workspace);
|
|
6986
7047
|
const channelSlug = normalizedWorkDescription(control.channel);
|
|
6987
7048
|
const kandanThreadId = normalizedWorkDescription(control.threadId);
|
|
@@ -7021,6 +7082,8 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
7021
7082
|
workspaceSlug,
|
|
7022
7083
|
channelSlug,
|
|
7023
7084
|
kandanThreadId,
|
|
7085
|
+
rootSeq: integerValue(control.rootSeq),
|
|
7086
|
+
codexThreadId,
|
|
7024
7087
|
listenUser,
|
|
7025
7088
|
model: control.model,
|
|
7026
7089
|
reasoningEffort: control.reasoningEffort,
|
|
@@ -7031,6 +7094,7 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
7031
7094
|
log
|
|
7032
7095
|
});
|
|
7033
7096
|
dynamicChannelSessions.set(kandanThreadId, session);
|
|
7097
|
+
return session;
|
|
7034
7098
|
};
|
|
7035
7099
|
const heartbeatPayload = () => ({
|
|
7036
7100
|
instanceId,
|
|
@@ -7420,14 +7484,23 @@ async function applyControl(codex, kandan, topic, instanceId, options, allowedCw
|
|
|
7420
7484
|
if (codexThreadId !== undefined && developerPrompt !== undefined) {
|
|
7421
7485
|
await postVisibleDeveloperPrompt(kandan, topic, control, developerPrompt, codexThreadId);
|
|
7422
7486
|
}
|
|
7423
|
-
|
|
7424
|
-
await onStartedThread(control, cwd.cwd);
|
|
7425
|
-
}
|
|
7487
|
+
const startedThreadSession = codexThreadId !== undefined && onStartedThread !== undefined ? await onStartedThread(control, cwd.cwd, codexThreadId) : undefined;
|
|
7426
7488
|
if (codexThreadId !== undefined && workDescription !== undefined) {
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7489
|
+
const rootSeq = integerValue(control.rootSeq);
|
|
7490
|
+
if (startedThreadSession !== undefined && rootSeq !== undefined) {
|
|
7491
|
+
const identity = identityFromAccessToken(options.token);
|
|
7492
|
+
await startedThreadSession.startThreadMessageTurn({
|
|
7493
|
+
seq: rootSeq,
|
|
7494
|
+
body: workDescription,
|
|
7495
|
+
actorSlug: identity.actorUsername,
|
|
7496
|
+
actorUserId: identity.actorUserId
|
|
7497
|
+
});
|
|
7498
|
+
} else {
|
|
7499
|
+
await codex.request("turn/start", {
|
|
7500
|
+
threadId: codexThreadId,
|
|
7501
|
+
input: [{ type: "text", text: workDescription }]
|
|
7502
|
+
});
|
|
7503
|
+
}
|
|
7431
7504
|
}
|
|
7432
7505
|
return {
|
|
7433
7506
|
instanceId,
|
|
@@ -7486,6 +7559,7 @@ async function applyControl(codex, kandan, topic, instanceId, options, allowedCw
|
|
|
7486
7559
|
case "kill_instance":
|
|
7487
7560
|
case "interrupt_queued_messages":
|
|
7488
7561
|
case "resolve_codex_approval_request":
|
|
7562
|
+
case "resolve_port_forward_request":
|
|
7489
7563
|
case "forward_http_request":
|
|
7490
7564
|
case "forward_websocket_open":
|
|
7491
7565
|
case "forward_websocket_send":
|
|
@@ -9421,7 +9495,7 @@ async function main(args) {
|
|
|
9421
9495
|
process.stdout.write(connectGuideText());
|
|
9422
9496
|
return;
|
|
9423
9497
|
case "version":
|
|
9424
|
-
process.stdout.write(`linzumi 0.0.
|
|
9498
|
+
process.stdout.write(`linzumi 0.0.37-beta
|
|
9425
9499
|
`);
|
|
9426
9500
|
return;
|
|
9427
9501
|
case "auth":
|
|
@@ -9935,7 +10009,7 @@ async function parseRunnerArgs(args, deps = {
|
|
|
9935
10009
|
process.exit(0);
|
|
9936
10010
|
}
|
|
9937
10011
|
if (values.get("version") === true) {
|
|
9938
|
-
process.stdout.write(`linzumi 0.0.
|
|
10012
|
+
process.stdout.write(`linzumi 0.0.37-beta
|
|
9939
10013
|
`);
|
|
9940
10014
|
process.exit(0);
|
|
9941
10015
|
}
|
package/package.json
CHANGED