@linzumi/cli 0.0.63-beta → 0.0.64-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 +465 -78
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -18214,6 +18214,35 @@ function codexFileChangeDeltaFromNotification(params) {
|
|
|
18214
18214
|
patchText
|
|
18215
18215
|
};
|
|
18216
18216
|
}
|
|
18217
|
+
function codexCompletedFileChangeFromNotification(params) {
|
|
18218
|
+
const item = objectValue(params.item) ?? objectValue(params.payload) ?? params;
|
|
18219
|
+
const status = stringValue(item.status);
|
|
18220
|
+
const turnId = stringValue(params.turnId) ?? stringValue(params.turn_id) ?? stringValue(objectValue(params.turn)?.id) ?? stringValue(item.turnId) ?? stringValue(item.turn_id) ?? stringValue(objectValue(item.turn)?.id);
|
|
18221
|
+
if (codexItemTypeIsFileChange(item) && (status === void 0 || status === "completed")) {
|
|
18222
|
+
const changes = normalizeFileChangeEntries(arrayValue(item.changes) ?? []);
|
|
18223
|
+
const patchText2 = nonBlankStringValue(item.patchText) ?? nonBlankStringValue(item.patch) ?? patchTextFromFileChangeEntries(changes);
|
|
18224
|
+
if (patchText2 === void 0) {
|
|
18225
|
+
return void 0;
|
|
18226
|
+
}
|
|
18227
|
+
return {
|
|
18228
|
+
itemKey: stringValue(item.id) ?? "file-change",
|
|
18229
|
+
turnId,
|
|
18230
|
+
patchText: patchText2
|
|
18231
|
+
};
|
|
18232
|
+
}
|
|
18233
|
+
if (stringValue(item.type) !== "custom_tool_call" || stringValue(item.name) !== "apply_patch" || status !== void 0 && status !== "completed") {
|
|
18234
|
+
return void 0;
|
|
18235
|
+
}
|
|
18236
|
+
const patchText = applyPatchTextFromValue(item.input) ?? applyPatchTextFromValue(item.arguments);
|
|
18237
|
+
if (patchText === void 0) {
|
|
18238
|
+
return void 0;
|
|
18239
|
+
}
|
|
18240
|
+
return {
|
|
18241
|
+
itemKey: stringValue(item.call_id) ?? stringValue(item.id) ?? "file-change",
|
|
18242
|
+
turnId,
|
|
18243
|
+
patchText
|
|
18244
|
+
};
|
|
18245
|
+
}
|
|
18217
18246
|
function codexFileChangeStructuredMessage(itemKey, patchText, streamState, status) {
|
|
18218
18247
|
return {
|
|
18219
18248
|
kind: "codex_file_change",
|
|
@@ -18413,20 +18442,20 @@ function commandMessageForFunctionCall(item, output, index) {
|
|
|
18413
18442
|
function messageForCustomToolCall(item, output, index) {
|
|
18414
18443
|
const name = stringValue(item.name) ?? "custom_tool_call";
|
|
18415
18444
|
const itemKey = stringValue(item.call_id) ?? stringValue(item.id) ?? `item-${index}`;
|
|
18416
|
-
const input = nonBlankStringValue(item.input) ?? nonBlankStringValue(item.arguments) ?? "";
|
|
18417
18445
|
if (name === "apply_patch") {
|
|
18446
|
+
const patchText = applyPatchTextFromValue(item.input) ?? applyPatchTextFromValue(item.arguments) ?? "";
|
|
18418
18447
|
return [
|
|
18419
18448
|
{
|
|
18420
18449
|
itemKey,
|
|
18421
|
-
body:
|
|
18450
|
+
body: patchText,
|
|
18422
18451
|
structured: {
|
|
18423
18452
|
kind: "codex_file_change",
|
|
18424
18453
|
item_id: itemKey,
|
|
18425
18454
|
transcript_unit_id: `codex_file_change:${itemKey}`,
|
|
18426
18455
|
stream_state: "completed",
|
|
18427
18456
|
status: output === void 0 ? "started" : "completed",
|
|
18428
|
-
patch_text:
|
|
18429
|
-
changes: fileChangesFromPatch(
|
|
18457
|
+
patch_text: patchText,
|
|
18458
|
+
changes: fileChangesFromPatch(patchText)
|
|
18430
18459
|
}
|
|
18431
18460
|
}
|
|
18432
18461
|
];
|
|
@@ -18477,43 +18506,120 @@ function toolOutputText(item) {
|
|
|
18477
18506
|
}
|
|
18478
18507
|
return nonBlankStringValue(item.output) ?? nonBlankStringValue(item.result) ?? nonBlankStringValue(item.content) ?? "";
|
|
18479
18508
|
}
|
|
18480
|
-
function
|
|
18481
|
-
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
18485
|
-
|
|
18486
|
-
|
|
18487
|
-
|
|
18488
|
-
path: line.slice(updatePrefix.length).trim(),
|
|
18489
|
-
diff: "",
|
|
18490
|
-
kind: "update",
|
|
18491
|
-
move_path: ""
|
|
18492
|
-
}
|
|
18493
|
-
];
|
|
18494
|
-
}
|
|
18495
|
-
if (line.startsWith(addPrefix)) {
|
|
18496
|
-
return [
|
|
18497
|
-
{
|
|
18498
|
-
path: line.slice(addPrefix.length).trim(),
|
|
18499
|
-
diff: "",
|
|
18500
|
-
kind: "add",
|
|
18501
|
-
move_path: ""
|
|
18502
|
-
}
|
|
18503
|
-
];
|
|
18509
|
+
function patchTextFromFileChangeEntries(changes) {
|
|
18510
|
+
if (changes.length === 0) {
|
|
18511
|
+
return void 0;
|
|
18512
|
+
}
|
|
18513
|
+
const body = changes.flatMap((change) => {
|
|
18514
|
+
const path2 = stringValue(change.path);
|
|
18515
|
+
if (path2 === void 0 || path2.trim() === "") {
|
|
18516
|
+
return [];
|
|
18504
18517
|
}
|
|
18505
|
-
|
|
18506
|
-
|
|
18507
|
-
|
|
18508
|
-
|
|
18509
|
-
|
|
18510
|
-
|
|
18511
|
-
|
|
18518
|
+
const kind = stringValue(change.kind);
|
|
18519
|
+
const header = kind === "add" ? `*** Add File: ${path2}` : kind === "delete" ? `*** Delete File: ${path2}` : `*** Update File: ${path2}`;
|
|
18520
|
+
const diff = nonBlankStringValue(change.diff);
|
|
18521
|
+
return diff === void 0 ? [header] : [header, diff];
|
|
18522
|
+
}).join("\n");
|
|
18523
|
+
return body === "" ? void 0 : `*** Begin Patch
|
|
18524
|
+
${body}
|
|
18525
|
+
*** End Patch
|
|
18526
|
+
`;
|
|
18527
|
+
}
|
|
18528
|
+
function fileChangesFromPatch(patchText) {
|
|
18529
|
+
const finalState = patchText.split("\n").reduce(
|
|
18530
|
+
(state, line) => {
|
|
18531
|
+
const header = patchHeaderForLine(line);
|
|
18532
|
+
if (header !== void 0) {
|
|
18533
|
+
return {
|
|
18534
|
+
changes: appendParsedPatchChange(state.changes, state.current),
|
|
18535
|
+
current: {
|
|
18536
|
+
path: header.path,
|
|
18537
|
+
kind: header.kind,
|
|
18538
|
+
movePath: "",
|
|
18539
|
+
diffLines: []
|
|
18540
|
+
}
|
|
18541
|
+
};
|
|
18542
|
+
}
|
|
18543
|
+
if (line.startsWith("*** Move to: ")) {
|
|
18544
|
+
const movePath = line.slice("*** Move to: ".length).trim();
|
|
18545
|
+
return {
|
|
18546
|
+
...state,
|
|
18547
|
+
current: state.current === void 0 ? void 0 : { ...state.current, movePath }
|
|
18548
|
+
};
|
|
18549
|
+
}
|
|
18550
|
+
if (line === "*** Begin Patch" || line === "*** End Patch" || line.startsWith("*** ")) {
|
|
18551
|
+
return state;
|
|
18552
|
+
}
|
|
18553
|
+
return {
|
|
18554
|
+
...state,
|
|
18555
|
+
current: state.current === void 0 ? void 0 : {
|
|
18556
|
+
...state.current,
|
|
18557
|
+
diffLines: [...state.current.diffLines, line]
|
|
18512
18558
|
}
|
|
18513
|
-
|
|
18514
|
-
}
|
|
18515
|
-
|
|
18516
|
-
|
|
18559
|
+
};
|
|
18560
|
+
},
|
|
18561
|
+
{ changes: [], current: void 0 }
|
|
18562
|
+
);
|
|
18563
|
+
return appendParsedPatchChange(finalState.changes, finalState.current).map(
|
|
18564
|
+
(change) => ({
|
|
18565
|
+
path: change.path,
|
|
18566
|
+
diff: trimDiffLines(change.diffLines),
|
|
18567
|
+
kind: change.kind,
|
|
18568
|
+
move_path: change.movePath
|
|
18569
|
+
})
|
|
18570
|
+
);
|
|
18571
|
+
}
|
|
18572
|
+
function patchHeaderForLine(line) {
|
|
18573
|
+
const updatePrefix = "*** Update File: ";
|
|
18574
|
+
const addPrefix = "*** Add File: ";
|
|
18575
|
+
const deletePrefix = "*** Delete File: ";
|
|
18576
|
+
if (line.startsWith(updatePrefix)) {
|
|
18577
|
+
return {
|
|
18578
|
+
path: line.slice(updatePrefix.length).trim(),
|
|
18579
|
+
kind: "update"
|
|
18580
|
+
};
|
|
18581
|
+
}
|
|
18582
|
+
if (line.startsWith(addPrefix)) {
|
|
18583
|
+
return {
|
|
18584
|
+
path: line.slice(addPrefix.length).trim(),
|
|
18585
|
+
kind: "add"
|
|
18586
|
+
};
|
|
18587
|
+
}
|
|
18588
|
+
if (line.startsWith(deletePrefix)) {
|
|
18589
|
+
return {
|
|
18590
|
+
path: line.slice(deletePrefix.length).trim(),
|
|
18591
|
+
kind: "delete"
|
|
18592
|
+
};
|
|
18593
|
+
}
|
|
18594
|
+
return void 0;
|
|
18595
|
+
}
|
|
18596
|
+
function appendParsedPatchChange(changes, change) {
|
|
18597
|
+
if (change === void 0 || change.path.trim() === "") {
|
|
18598
|
+
return [...changes];
|
|
18599
|
+
}
|
|
18600
|
+
return [...changes, change];
|
|
18601
|
+
}
|
|
18602
|
+
function trimDiffLines(lines) {
|
|
18603
|
+
const firstContentIndex = lines.findIndex((line) => line.trim() !== "");
|
|
18604
|
+
if (firstContentIndex < 0) {
|
|
18605
|
+
return "";
|
|
18606
|
+
}
|
|
18607
|
+
const lastContentIndexFromEnd = [...lines].reverse().findIndex((line) => line.trim() !== "");
|
|
18608
|
+
const end = lastContentIndexFromEnd < 0 ? lines.length : lines.length - lastContentIndexFromEnd;
|
|
18609
|
+
return lines.slice(firstContentIndex, end).join("\n");
|
|
18610
|
+
}
|
|
18611
|
+
function codexItemTypeIsFileChange(item) {
|
|
18612
|
+
const itemType = stringValue(item.type);
|
|
18613
|
+
return itemType === "fileChange" || itemType === "file_change";
|
|
18614
|
+
}
|
|
18615
|
+
function applyPatchTextFromValue(value) {
|
|
18616
|
+
const text2 = nonBlankStringValue(value);
|
|
18617
|
+
if (text2 !== void 0) {
|
|
18618
|
+
const parsed = parseJsonObjectOrUndefined(text2);
|
|
18619
|
+
return nonBlankStringValue(parsed?.patch) ?? nonBlankStringValue(parsed?.input) ?? nonBlankStringValue(parsed?.text) ?? text2;
|
|
18620
|
+
}
|
|
18621
|
+
const object = objectValue(value);
|
|
18622
|
+
return nonBlankStringValue(object?.patch) ?? nonBlankStringValue(object?.input) ?? nonBlankStringValue(object?.text);
|
|
18517
18623
|
}
|
|
18518
18624
|
function webSearchQueriesForItem(item) {
|
|
18519
18625
|
const action = objectValue(item.action);
|
|
@@ -19192,7 +19298,13 @@ import { basename as basename2 } from "node:path";
|
|
|
19192
19298
|
var defaultIntervalMs = 2e3;
|
|
19193
19299
|
var defaultDebounceMs = 750;
|
|
19194
19300
|
function startPortForwardWatcher(options) {
|
|
19195
|
-
|
|
19301
|
+
if (options.rootPid === void 0) {
|
|
19302
|
+
throw new Error("port_forward_watcher_root_pid_missing");
|
|
19303
|
+
}
|
|
19304
|
+
if (!Number.isInteger(options.rootPid) || options.rootPid <= 0) {
|
|
19305
|
+
throw new Error("port_forward_watcher_root_pid_invalid");
|
|
19306
|
+
}
|
|
19307
|
+
const rootPid = options.rootPid;
|
|
19196
19308
|
const intervalMs = options.intervalMs ?? defaultIntervalMs;
|
|
19197
19309
|
const debounceMs = options.debounceMs ?? defaultDebounceMs;
|
|
19198
19310
|
const lostDebounceMs = options.lostDebounceMs ?? intervalMs * 2 + debounceMs;
|
|
@@ -19314,7 +19426,7 @@ function stableForwardCandidates(previousObservedByPort, candidates, nowMs, debo
|
|
|
19314
19426
|
return { nextObservedByPort, stableCandidates };
|
|
19315
19427
|
}
|
|
19316
19428
|
function sameForwardCandidate(left, right) {
|
|
19317
|
-
return left.port === right.port && left.pid === right.pid
|
|
19429
|
+
return left.port === right.port && left.pid === right.pid;
|
|
19318
19430
|
}
|
|
19319
19431
|
function descendantPidSet(rows, rootPid) {
|
|
19320
19432
|
const childrenByParent = /* @__PURE__ */ new Map();
|
|
@@ -19480,16 +19592,6 @@ function reviewPortForwardCandidate(options) {
|
|
|
19480
19592
|
if (!options.threadBound) {
|
|
19481
19593
|
return { type: "skip", reason: "thread_not_bound" };
|
|
19482
19594
|
}
|
|
19483
|
-
if (options.suppressedPorts?.has(options.candidate.port) === true) {
|
|
19484
|
-
return { type: "skip", reason: "suppressed_port" };
|
|
19485
|
-
}
|
|
19486
|
-
if (isInternalCodexProcess(options.candidate)) {
|
|
19487
|
-
return { type: "skip", reason: "internal_codex_process" };
|
|
19488
|
-
}
|
|
19489
|
-
const dismissedTarget = options.dismissedTargets?.get(options.candidate.port);
|
|
19490
|
-
if (dismissedTarget !== void 0 && sameDismissedPortForwardTarget(dismissedTarget, options.candidate)) {
|
|
19491
|
-
return { type: "skip", reason: "recently_dismissed" };
|
|
19492
|
-
}
|
|
19493
19595
|
if (options.approvedPorts.has(options.candidate.port)) {
|
|
19494
19596
|
const approvedTarget = options.approvedTargets.get(options.candidate.port);
|
|
19495
19597
|
if (approvedTarget === void 0) {
|
|
@@ -19568,12 +19670,6 @@ function revocationCapabilities(capabilities, port) {
|
|
|
19568
19670
|
revokedPorts: [port]
|
|
19569
19671
|
};
|
|
19570
19672
|
}
|
|
19571
|
-
function sameDismissedPortForwardTarget(left, right) {
|
|
19572
|
-
return left.port === right.port && left.command === right.command && left.cwd === right.cwd;
|
|
19573
|
-
}
|
|
19574
|
-
function isInternalCodexProcess(candidate) {
|
|
19575
|
-
return commandLabel(candidate.command) === "codex";
|
|
19576
|
-
}
|
|
19577
19673
|
|
|
19578
19674
|
// src/processNameCatalog.ts
|
|
19579
19675
|
var processCatalogEntries = [
|
|
@@ -20259,20 +20355,21 @@ async function attachChannelSession(args) {
|
|
|
20259
20355
|
handleCodexNotification: (method, params) => {
|
|
20260
20356
|
const turnId = codexNotificationTurnId(params);
|
|
20261
20357
|
const threadId = codexNotificationThreadId(params);
|
|
20262
|
-
|
|
20358
|
+
const routedTurnId = turnId ?? codexNotificationActiveTurnFallback(method, state, params);
|
|
20359
|
+
if (codexNotificationBelongsToSession(state, threadId, routedTurnId)) {
|
|
20263
20360
|
const processingReason = processingReasonForCodexNotification(
|
|
20264
20361
|
method,
|
|
20265
20362
|
params
|
|
20266
20363
|
);
|
|
20267
|
-
if (
|
|
20364
|
+
if (routedTurnId !== void 0 && processingReason !== void 0) {
|
|
20268
20365
|
void refreshActiveProcessingState(
|
|
20269
20366
|
args,
|
|
20270
20367
|
state,
|
|
20271
|
-
|
|
20368
|
+
routedTurnId,
|
|
20272
20369
|
processingReason
|
|
20273
20370
|
).catch((error) => {
|
|
20274
20371
|
args.log("kandan.message_state_refresh_failed", {
|
|
20275
|
-
turn_id:
|
|
20372
|
+
turn_id: routedTurnId,
|
|
20276
20373
|
reason: processingReason,
|
|
20277
20374
|
message: error instanceof Error ? error.message : String(error)
|
|
20278
20375
|
});
|
|
@@ -20341,6 +20438,7 @@ async function attachChannelSession(args) {
|
|
|
20341
20438
|
break;
|
|
20342
20439
|
case "item/completed":
|
|
20343
20440
|
enqueueWebSearchProgress(args, state, params, payloadContext);
|
|
20441
|
+
enqueueCompletedFileChange(args, state, params, payloadContext);
|
|
20344
20442
|
if (turnId !== void 0) {
|
|
20345
20443
|
const promise = mirrorLocalTuiInputFromNotification(
|
|
20346
20444
|
args,
|
|
@@ -20358,6 +20456,9 @@ async function attachChannelSession(args) {
|
|
|
20358
20456
|
}).finally(() => forgetPendingTuiInputMirror(state, turnId));
|
|
20359
20457
|
}
|
|
20360
20458
|
break;
|
|
20459
|
+
case "response_item":
|
|
20460
|
+
enqueueCompletedFileChange(args, state, params, payloadContext);
|
|
20461
|
+
break;
|
|
20361
20462
|
}
|
|
20362
20463
|
}
|
|
20363
20464
|
},
|
|
@@ -20448,6 +20549,7 @@ function initialChannelSessionState(cursor, rootSeq, kandanThreadId, codexThread
|
|
|
20448
20549
|
webSearchProgressOutputs: createBoundedCache(maxForwardedTurnIds),
|
|
20449
20550
|
pendingApprovalRequests: /* @__PURE__ */ new Map(),
|
|
20450
20551
|
approvalPromptChain: Promise.resolve(),
|
|
20552
|
+
approvedThreadInteractionRequesters: /* @__PURE__ */ new Set(),
|
|
20451
20553
|
pendingPortForwardRequests: /* @__PURE__ */ new Map(),
|
|
20452
20554
|
portForwardPreviousProcessingStates: /* @__PURE__ */ new Map(),
|
|
20453
20555
|
queuedPortForwardCandidates: /* @__PURE__ */ new Map(),
|
|
@@ -20497,6 +20599,12 @@ function startPortForwardWatchIfEnabled(args, state, payloadContext) {
|
|
|
20497
20599
|
return;
|
|
20498
20600
|
}
|
|
20499
20601
|
const { start: configuredStart, ...watchOptions } = args.options.portForwardWatcher ?? {};
|
|
20602
|
+
if (configuredStart === void 0 && watchOptions.rootPid === void 0) {
|
|
20603
|
+
args.log("port_forward.watch_skipped", {
|
|
20604
|
+
reason: "worker_pid_missing"
|
|
20605
|
+
});
|
|
20606
|
+
return;
|
|
20607
|
+
}
|
|
20500
20608
|
const start = configuredStart ?? startPortForwardWatcher;
|
|
20501
20609
|
for (const port of args.options.initialForwardPorts ?? []) {
|
|
20502
20610
|
state.approvedForwardPorts.add(port);
|
|
@@ -20572,6 +20680,9 @@ async function handleChannelSessionControl(args, state, payloadContext, control)
|
|
|
20572
20680
|
control
|
|
20573
20681
|
);
|
|
20574
20682
|
}
|
|
20683
|
+
if (control.type === "update_thread_interaction_access") {
|
|
20684
|
+
return updateThreadInteractionAccess(args, state, control);
|
|
20685
|
+
}
|
|
20575
20686
|
if (control.type !== "interrupt_queued_messages") {
|
|
20576
20687
|
return void 0;
|
|
20577
20688
|
}
|
|
@@ -20639,6 +20750,76 @@ async function handleChannelSessionControl(args, state, payloadContext, control)
|
|
|
20639
20750
|
interruptedQueuedMessages: interrupted.ok ? interrupted.selectedCount : 0
|
|
20640
20751
|
};
|
|
20641
20752
|
}
|
|
20753
|
+
async function updateThreadInteractionAccess(args, state, control) {
|
|
20754
|
+
const requesterKeys = threadInteractionRequesterKeys(control);
|
|
20755
|
+
if (state.kandanThreadId !== control.threadId) {
|
|
20756
|
+
args.log("thread_interaction.access_update_ignored", {
|
|
20757
|
+
thread_id: control.threadId,
|
|
20758
|
+
expected_thread_id: state.kandanThreadId ?? null,
|
|
20759
|
+
requester_slug: control.requesterSlug ?? null,
|
|
20760
|
+
requester_user_id: control.requesterUserId ?? null,
|
|
20761
|
+
reason: "different_thread"
|
|
20762
|
+
});
|
|
20763
|
+
return {
|
|
20764
|
+
instanceId: args.instanceId,
|
|
20765
|
+
ok: false,
|
|
20766
|
+
error: "different_thread"
|
|
20767
|
+
};
|
|
20768
|
+
}
|
|
20769
|
+
if (requesterKeys.length === 0) {
|
|
20770
|
+
args.log("thread_interaction.access_update_ignored", {
|
|
20771
|
+
thread_id: control.threadId,
|
|
20772
|
+
requester_slug: control.requesterSlug ?? null,
|
|
20773
|
+
requester_user_id: control.requesterUserId ?? null,
|
|
20774
|
+
reason: "missing_requester"
|
|
20775
|
+
});
|
|
20776
|
+
return {
|
|
20777
|
+
instanceId: args.instanceId,
|
|
20778
|
+
ok: false,
|
|
20779
|
+
error: "missing_requester"
|
|
20780
|
+
};
|
|
20781
|
+
}
|
|
20782
|
+
for (const requesterKey of requesterKeys) {
|
|
20783
|
+
if (control.grant) {
|
|
20784
|
+
state.approvedThreadInteractionRequesters.add(requesterKey);
|
|
20785
|
+
} else {
|
|
20786
|
+
state.approvedThreadInteractionRequesters.delete(requesterKey);
|
|
20787
|
+
}
|
|
20788
|
+
}
|
|
20789
|
+
args.log("thread_interaction.access_updated", {
|
|
20790
|
+
thread_id: control.threadId,
|
|
20791
|
+
requester_slug: control.requesterSlug ?? null,
|
|
20792
|
+
requester_user_id: control.requesterUserId ?? null,
|
|
20793
|
+
grant: control.grant
|
|
20794
|
+
});
|
|
20795
|
+
return {
|
|
20796
|
+
instanceId: args.instanceId,
|
|
20797
|
+
ok: true,
|
|
20798
|
+
grant: control.grant
|
|
20799
|
+
};
|
|
20800
|
+
}
|
|
20801
|
+
function threadInteractionRequesterKeys(args) {
|
|
20802
|
+
if (args.threadId === void 0) {
|
|
20803
|
+
return [];
|
|
20804
|
+
}
|
|
20805
|
+
const keys = [];
|
|
20806
|
+
if (args.requesterUserId !== void 0) {
|
|
20807
|
+
keys.push(`${args.threadId}:user:${args.requesterUserId}`);
|
|
20808
|
+
}
|
|
20809
|
+
if (args.requesterSlug !== void 0 && args.requesterSlug.trim() !== "") {
|
|
20810
|
+
keys.push(`${args.threadId}:slug:${args.requesterSlug}`);
|
|
20811
|
+
}
|
|
20812
|
+
return keys;
|
|
20813
|
+
}
|
|
20814
|
+
function threadInteractionSenderAllowed(state, event) {
|
|
20815
|
+
return threadInteractionRequesterKeys({
|
|
20816
|
+
threadId: event.threadId,
|
|
20817
|
+
requesterUserId: event.actorUserId,
|
|
20818
|
+
requesterSlug: event.actorSlug
|
|
20819
|
+
}).some(
|
|
20820
|
+
(requesterKey) => state.approvedThreadInteractionRequesters.has(requesterKey)
|
|
20821
|
+
);
|
|
20822
|
+
}
|
|
20642
20823
|
async function updateSessionSettings(args, state, payloadContext, control) {
|
|
20643
20824
|
if (state.codexThreadId !== control.threadId) {
|
|
20644
20825
|
return void 0;
|
|
@@ -21337,13 +21518,16 @@ function parsePortForwardDecision(body) {
|
|
|
21337
21518
|
}
|
|
21338
21519
|
async function handleKandanChatEvent(args, state, runnerIdentity, payloadContext, event) {
|
|
21339
21520
|
const session = args.options.channelSession;
|
|
21340
|
-
if (event.type !== "thread.message"
|
|
21341
|
-
args
|
|
21342
|
-
|
|
21343
|
-
|
|
21344
|
-
|
|
21345
|
-
|
|
21346
|
-
|
|
21521
|
+
if (event.type !== "thread.message") {
|
|
21522
|
+
logIgnoredKandanMessage(args, event, "non_thread_message_event");
|
|
21523
|
+
return;
|
|
21524
|
+
}
|
|
21525
|
+
if (event.threadId === void 0) {
|
|
21526
|
+
logIgnoredKandanMessage(args, event, "missing_thread_id");
|
|
21527
|
+
return;
|
|
21528
|
+
}
|
|
21529
|
+
if (event.body.trim() === "" && event.attachments.length === 0) {
|
|
21530
|
+
logIgnoredKandanMessage(args, event, "empty_thread_message");
|
|
21347
21531
|
return;
|
|
21348
21532
|
}
|
|
21349
21533
|
if (isCodexAuthoredEvent(event)) {
|
|
@@ -21355,7 +21539,7 @@ async function handleKandanChatEvent(args, state, runnerIdentity, payloadContext
|
|
|
21355
21539
|
});
|
|
21356
21540
|
return;
|
|
21357
21541
|
}
|
|
21358
|
-
if (!senderAllowed(session.listenUser, event, runnerIdentity)) {
|
|
21542
|
+
if (!senderAllowed(session.listenUser, event, runnerIdentity) && !threadInteractionSenderAllowed(state, event)) {
|
|
21359
21543
|
args.log("kandan.message_ignored", {
|
|
21360
21544
|
seq: event.seq,
|
|
21361
21545
|
actor_slug: event.actorSlug ?? null,
|
|
@@ -21466,6 +21650,19 @@ async function handleKandanChatEvent(args, state, runnerIdentity, payloadContext
|
|
|
21466
21650
|
await publishKandanMessageState(args, event, { status: "queued" });
|
|
21467
21651
|
await drainKandanMessageQueue(args, state, payloadContext);
|
|
21468
21652
|
}
|
|
21653
|
+
function logIgnoredKandanMessage(args, event, reason) {
|
|
21654
|
+
args.log("kandan.message_ignored", {
|
|
21655
|
+
seq: event.seq,
|
|
21656
|
+
type: event.type,
|
|
21657
|
+
thread_id: event.threadId ?? null,
|
|
21658
|
+
actor_slug: event.actorSlug ?? null,
|
|
21659
|
+
actor_user_id: event.actorUserId ?? null,
|
|
21660
|
+
body_length: event.body.length,
|
|
21661
|
+
attachment_count: event.attachments.length,
|
|
21662
|
+
local_runner_event_type: event.localRunnerEventType ?? null,
|
|
21663
|
+
reason
|
|
21664
|
+
});
|
|
21665
|
+
}
|
|
21469
21666
|
function kandanMessageClaimKey(args, threadId, seq) {
|
|
21470
21667
|
const session = args.options.channelSession;
|
|
21471
21668
|
return [
|
|
@@ -22079,6 +22276,7 @@ function completedOutputProjectionsForTurn(state, turnId, messages) {
|
|
|
22079
22276
|
}),
|
|
22080
22277
|
...cachedOutputs.map((output) => output.sortOrder)
|
|
22081
22278
|
]);
|
|
22279
|
+
const fallbackSnapshotPositions = fallbackSnapshotOutputPositions(snapshotOutputs);
|
|
22082
22280
|
const orderedSnapshotOutputs = snapshotOutputs.map((output) => {
|
|
22083
22281
|
switch (output.match.kind) {
|
|
22084
22282
|
case "none":
|
|
@@ -22086,7 +22284,7 @@ function completedOutputProjectionsForTurn(state, turnId, messages) {
|
|
|
22086
22284
|
...output,
|
|
22087
22285
|
sortOrder: fallbackSnapshotOutputOrder(
|
|
22088
22286
|
fallbackSortOrderBase,
|
|
22089
|
-
output
|
|
22287
|
+
fallbackSnapshotPosition(fallbackSnapshotPositions, output)
|
|
22090
22288
|
)
|
|
22091
22289
|
};
|
|
22092
22290
|
case "assistant":
|
|
@@ -22288,8 +22486,44 @@ function compareCompletedOutputProjection(left, right) {
|
|
|
22288
22486
|
function fallbackSnapshotOutputBase(sortOrders) {
|
|
22289
22487
|
return sortOrders.reduce((max, sortOrder) => Math.max(max, sortOrder), 0) + 1;
|
|
22290
22488
|
}
|
|
22291
|
-
function fallbackSnapshotOutputOrder(base,
|
|
22292
|
-
return base +
|
|
22489
|
+
function fallbackSnapshotOutputOrder(base, fallbackPosition) {
|
|
22490
|
+
return base + fallbackPosition;
|
|
22491
|
+
}
|
|
22492
|
+
function fallbackSnapshotOutputPositions(outputs) {
|
|
22493
|
+
const unmatched = outputs.filter((output) => output.match.kind === "none");
|
|
22494
|
+
const firstAssistantIndex = unmatched.findIndex(
|
|
22495
|
+
(output) => stringValue(output.message.structured.kind) === "codex_assistant_message"
|
|
22496
|
+
);
|
|
22497
|
+
if (firstAssistantIndex < 0) {
|
|
22498
|
+
return snapshotOutputPositionMap(unmatched);
|
|
22499
|
+
}
|
|
22500
|
+
const beforeFirstAssistant = unmatched.slice(0, firstAssistantIndex);
|
|
22501
|
+
const afterFirstAssistant = unmatched.slice(firstAssistantIndex);
|
|
22502
|
+
const fileChangesAfterAssistant = afterFirstAssistant.filter(
|
|
22503
|
+
(output) => stringValue(output.message.structured.kind) === "codex_file_change"
|
|
22504
|
+
);
|
|
22505
|
+
const remainingAfterAssistant = afterFirstAssistant.filter(
|
|
22506
|
+
(output) => stringValue(output.message.structured.kind) !== "codex_file_change"
|
|
22507
|
+
);
|
|
22508
|
+
return snapshotOutputPositionMap([
|
|
22509
|
+
...beforeFirstAssistant,
|
|
22510
|
+
...fileChangesAfterAssistant,
|
|
22511
|
+
...remainingAfterAssistant
|
|
22512
|
+
]);
|
|
22513
|
+
}
|
|
22514
|
+
function snapshotOutputPositionMap(outputs) {
|
|
22515
|
+
return new Map(
|
|
22516
|
+
outputs.map((output, position) => [output.snapshotIndex, position])
|
|
22517
|
+
);
|
|
22518
|
+
}
|
|
22519
|
+
function fallbackSnapshotPosition(positions, output) {
|
|
22520
|
+
const position = positions.get(output.snapshotIndex);
|
|
22521
|
+
if (position === void 0) {
|
|
22522
|
+
throw new Error(
|
|
22523
|
+
`Missing fallback completed-output position for snapshot index ${output.snapshotIndex}`
|
|
22524
|
+
);
|
|
22525
|
+
}
|
|
22526
|
+
return position;
|
|
22293
22527
|
}
|
|
22294
22528
|
function structuredOutputMatchKey(kind, itemKey) {
|
|
22295
22529
|
return `${kind}:${itemKey}`;
|
|
@@ -22527,6 +22761,27 @@ function enqueueFileChangeCompletion(args, state, turnId) {
|
|
|
22527
22761
|
});
|
|
22528
22762
|
});
|
|
22529
22763
|
}
|
|
22764
|
+
function enqueueCompletedFileChange(args, state, params, payloadContext) {
|
|
22765
|
+
const delta = codexCompletedFileChangeFromNotification(params);
|
|
22766
|
+
if (delta === void 0) {
|
|
22767
|
+
return;
|
|
22768
|
+
}
|
|
22769
|
+
flushStreamDeltaQueue(
|
|
22770
|
+
state.fileChangeQueue,
|
|
22771
|
+
fileChangeQueueRuntime(args, state, payloadContext)
|
|
22772
|
+
);
|
|
22773
|
+
const previous = state.fileChangeQueue.chain;
|
|
22774
|
+
const next = previous.catch(() => void 0).then(
|
|
22775
|
+
() => forwardCompletedFileChangePayload(args, state, delta, payloadContext)
|
|
22776
|
+
);
|
|
22777
|
+
state.fileChangeQueue.chain = next.catch((error) => {
|
|
22778
|
+
args.log("codex.completed_file_change_forward_failed", {
|
|
22779
|
+
turn_id: delta.turnId ?? null,
|
|
22780
|
+
item_key: delta.itemKey,
|
|
22781
|
+
message: error instanceof Error ? error.message : String(error)
|
|
22782
|
+
});
|
|
22783
|
+
});
|
|
22784
|
+
}
|
|
22530
22785
|
async function forwardReasoningDeltaPayload(args, state, delta, payloadContext) {
|
|
22531
22786
|
if (state.kandanThreadId === void 0 || state.codexThreadId === void 0) {
|
|
22532
22787
|
return;
|
|
@@ -22794,6 +23049,87 @@ async function forwardFileChangeDeltaPayload(args, state, delta, payloadContext)
|
|
|
22794
23049
|
patch_length: patchText.length
|
|
22795
23050
|
});
|
|
22796
23051
|
}
|
|
23052
|
+
async function forwardCompletedFileChangePayload(args, state, delta, payloadContext) {
|
|
23053
|
+
if (state.kandanThreadId === void 0 || state.codexThreadId === void 0) {
|
|
23054
|
+
return;
|
|
23055
|
+
}
|
|
23056
|
+
const turnId = delta.turnId ?? activeTurnId(state.turn);
|
|
23057
|
+
if (turnId === void 0 || turnIsFinalizingOrForwarded(state, turnId)) {
|
|
23058
|
+
return;
|
|
23059
|
+
}
|
|
23060
|
+
const sourceMessageSeq = sourceMessageSeqForTurn(state, turnId);
|
|
23061
|
+
if (sourceMessageSeq === void 0) {
|
|
23062
|
+
args.log("codex.completed_file_change_without_source_message", {
|
|
23063
|
+
turn_id: turnId,
|
|
23064
|
+
item_key: delta.itemKey
|
|
23065
|
+
});
|
|
23066
|
+
return;
|
|
23067
|
+
}
|
|
23068
|
+
const structured = codexFileChangeStructuredMessage(
|
|
23069
|
+
delta.itemKey,
|
|
23070
|
+
delta.patchText,
|
|
23071
|
+
"completed",
|
|
23072
|
+
"completed"
|
|
23073
|
+
);
|
|
23074
|
+
const existing = findStreamingFileChangeOutput(state, delta.itemKey);
|
|
23075
|
+
if (existing === void 0) {
|
|
23076
|
+
const session = args.options.channelSession;
|
|
23077
|
+
const reply = await pushOk2(
|
|
23078
|
+
args.kandan,
|
|
23079
|
+
args.topic,
|
|
23080
|
+
"session:post_thread_message",
|
|
23081
|
+
{
|
|
23082
|
+
workspace: session.workspaceSlug,
|
|
23083
|
+
channel: session.channelSlug,
|
|
23084
|
+
thread_id: state.kandanThreadId,
|
|
23085
|
+
body: delta.patchText,
|
|
23086
|
+
payload: {
|
|
23087
|
+
...localRunnerPayload(
|
|
23088
|
+
args.options,
|
|
23089
|
+
args.instanceId,
|
|
23090
|
+
"codex_output",
|
|
23091
|
+
state.codexThreadId,
|
|
23092
|
+
payloadContext,
|
|
23093
|
+
sourceMessageSeq
|
|
23094
|
+
),
|
|
23095
|
+
...state.rootSeq === void 0 ? {} : { reply_to_seq: state.rootSeq },
|
|
23096
|
+
structured
|
|
23097
|
+
},
|
|
23098
|
+
client_message_id: streamingClientMessageId(args.instanceId, {
|
|
23099
|
+
itemKey: `file-change:${delta.itemKey}`,
|
|
23100
|
+
turnId
|
|
23101
|
+
})
|
|
23102
|
+
}
|
|
23103
|
+
);
|
|
23104
|
+
const seq = integerValue(reply.seq);
|
|
23105
|
+
if (seq !== void 0) {
|
|
23106
|
+
rememberStreamingFileChangeOutput(state, {
|
|
23107
|
+
itemKey: delta.itemKey,
|
|
23108
|
+
turnId,
|
|
23109
|
+
seq,
|
|
23110
|
+
patchText: delta.patchText
|
|
23111
|
+
});
|
|
23112
|
+
}
|
|
23113
|
+
} else {
|
|
23114
|
+
await editCodexStructuredOutput(
|
|
23115
|
+
args,
|
|
23116
|
+
state,
|
|
23117
|
+
existing.seq,
|
|
23118
|
+
delta.patchText,
|
|
23119
|
+
structured
|
|
23120
|
+
);
|
|
23121
|
+
rememberStreamingFileChangeOutput(state, {
|
|
23122
|
+
...existing,
|
|
23123
|
+
turnId: existing.turnId ?? turnId,
|
|
23124
|
+
patchText: delta.patchText
|
|
23125
|
+
});
|
|
23126
|
+
}
|
|
23127
|
+
args.log("kandan.codex_file_change_forwarded", {
|
|
23128
|
+
item_key: delta.itemKey,
|
|
23129
|
+
turn_id: turnId,
|
|
23130
|
+
patch_length: delta.patchText.length
|
|
23131
|
+
});
|
|
23132
|
+
}
|
|
22797
23133
|
async function forwardTerminalInput(args, state, params, payloadContext) {
|
|
22798
23134
|
if (state.kandanThreadId === void 0 || state.codexThreadId === void 0) {
|
|
22799
23135
|
return;
|
|
@@ -23342,6 +23678,12 @@ function codexNotificationBelongsToSession(state, threadId, turnId) {
|
|
|
23342
23678
|
}
|
|
23343
23679
|
return activeTurnId(state.turn) === turnId || isLocalTuiTurn(state, turnId);
|
|
23344
23680
|
}
|
|
23681
|
+
function codexNotificationActiveTurnFallback(method, state, params) {
|
|
23682
|
+
if (method !== "response_item" || codexCompletedFileChangeFromNotification(params) === void 0) {
|
|
23683
|
+
return void 0;
|
|
23684
|
+
}
|
|
23685
|
+
return activeTurnId(state.turn);
|
|
23686
|
+
}
|
|
23345
23687
|
function rememberLocalTuiTurnIfNeeded(args, state, threadId, turnId) {
|
|
23346
23688
|
if (args.options.launchTui !== true || state.codexThreadId !== threadId || state.turn.status !== "idle") {
|
|
23347
23689
|
return;
|
|
@@ -24598,6 +24940,9 @@ function defaultCliAuditLogFile() {
|
|
|
24598
24940
|
const override = process.env.LINZUMI_CLI_AUDIT_LOG?.trim();
|
|
24599
24941
|
return override === void 0 || override === "" ? join3(homedir2(), ".linzumi", "logs", "command-events.jsonl") : override;
|
|
24600
24942
|
}
|
|
24943
|
+
function defaultRunnerLogFile() {
|
|
24944
|
+
return join3(homedir2(), ".linzumi", "logs", "runner-events.jsonl");
|
|
24945
|
+
}
|
|
24601
24946
|
function redactForCliLog(value) {
|
|
24602
24947
|
return redactObject(value);
|
|
24603
24948
|
}
|
|
@@ -28295,7 +28640,7 @@ function realpathOrResolved(pathValue) {
|
|
|
28295
28640
|
}
|
|
28296
28641
|
|
|
28297
28642
|
// src/version.ts
|
|
28298
|
-
var linzumiCliVersion = "0.0.
|
|
28643
|
+
var linzumiCliVersion = "0.0.64-beta";
|
|
28299
28644
|
var linzumiCliVersionText = `linzumi ${linzumiCliVersion}`;
|
|
28300
28645
|
|
|
28301
28646
|
// src/runnerLock.ts
|
|
@@ -28565,7 +28910,7 @@ function formatRunnerConsoleEvent(event, payload) {
|
|
|
28565
28910
|
"This process is exiting."
|
|
28566
28911
|
].join("\n");
|
|
28567
28912
|
case "kandan.message_ignored":
|
|
28568
|
-
return
|
|
28913
|
+
return ignoredMessage(payload);
|
|
28569
28914
|
case "kandan.message_queued":
|
|
28570
28915
|
return `Incoming message from ${sender(payload)}: queued seq=${text(payload.seq)} depth=${text(payload.queue_depth)}`;
|
|
28571
28916
|
case "kandan.chat_event_failed":
|
|
@@ -28613,6 +28958,22 @@ function optionalLine(label, value) {
|
|
|
28613
28958
|
const normalized = stringValue2(value) ?? numberValue(value)?.toString();
|
|
28614
28959
|
return normalized === void 0 ? void 0 : `${label}: ${normalized}`;
|
|
28615
28960
|
}
|
|
28961
|
+
function optionalField(label, value) {
|
|
28962
|
+
const normalized = stringValue2(value) ?? numberValue(value)?.toString();
|
|
28963
|
+
return normalized === void 0 ? void 0 : `${label}=${normalized}`;
|
|
28964
|
+
}
|
|
28965
|
+
function ignoredMessage(payload) {
|
|
28966
|
+
return [
|
|
28967
|
+
`Incoming message from ${sender(payload)}: ignored`,
|
|
28968
|
+
optionalField("seq", payload.seq),
|
|
28969
|
+
`reason=${text(payload.reason)}`,
|
|
28970
|
+
optionalField("type", payload.type),
|
|
28971
|
+
optionalField("thread", payload.thread_id),
|
|
28972
|
+
optionalField("body_chars", payload.body_length),
|
|
28973
|
+
optionalField("attachments", payload.attachment_count),
|
|
28974
|
+
optionalField("local_event", payload.local_runner_event_type)
|
|
28975
|
+
].filter((part) => part !== void 0).join(" ");
|
|
28976
|
+
}
|
|
28616
28977
|
function replacementLines(value) {
|
|
28617
28978
|
if (!Array.isArray(value)) {
|
|
28618
28979
|
return [];
|
|
@@ -29945,9 +30306,16 @@ async function openLocalCodexRunner(options, log, cleanup, close) {
|
|
|
29945
30306
|
if (claudeCodeForwardSessions.has(args.sessionId)) {
|
|
29946
30307
|
return;
|
|
29947
30308
|
}
|
|
30309
|
+
if (options.portForwardWatcher === void 0) {
|
|
30310
|
+
log("port_forward.claude_code_watch_skipped", {
|
|
30311
|
+
reason: "worker_pid_missing",
|
|
30312
|
+
claude_session_id: args.sessionId
|
|
30313
|
+
});
|
|
30314
|
+
return;
|
|
30315
|
+
}
|
|
29948
30316
|
const session = {
|
|
29949
30317
|
...args,
|
|
29950
|
-
watcher:
|
|
30318
|
+
watcher: options.portForwardWatcher({
|
|
29951
30319
|
onCandidate: (candidate) => publishClaudeCodeForwardPortPrompt(session, candidate),
|
|
29952
30320
|
onCandidateLost: (candidate) => expireLostClaudeCodeForwardCandidate(session, candidate),
|
|
29953
30321
|
onError: (error) => {
|
|
@@ -30779,7 +31147,8 @@ function controlThreadId(control) {
|
|
|
30779
31147
|
case "forward_http_request":
|
|
30780
31148
|
case "forward_tcp_open":
|
|
30781
31149
|
case "forward_tcp_send":
|
|
30782
|
-
case "forward_tcp_close":
|
|
31150
|
+
case "forward_tcp_close":
|
|
31151
|
+
case "update_thread_interaction_access": {
|
|
30783
31152
|
const threadId = stringValue(control.threadId)?.trim();
|
|
30784
31153
|
return threadId === void 0 || threadId === "" ? void 0 : threadId;
|
|
30785
31154
|
}
|
|
@@ -30788,6 +31157,23 @@ function controlThreadId(control) {
|
|
|
30788
31157
|
}
|
|
30789
31158
|
}
|
|
30790
31159
|
async function resolveSessionControl(channelSession, dynamicChannelSessions, control) {
|
|
31160
|
+
const targetThreadId = controlThreadId(control);
|
|
31161
|
+
if (targetThreadId !== void 0) {
|
|
31162
|
+
const targetDynamicSession = dynamicChannelSessions.get(targetThreadId);
|
|
31163
|
+
const targetSessions = [
|
|
31164
|
+
...channelSession?.currentKandanThreadId() === targetThreadId ? [channelSession] : [],
|
|
31165
|
+
...targetDynamicSession !== void 0 && targetDynamicSession !== channelSession ? [targetDynamicSession] : []
|
|
31166
|
+
].filter(
|
|
31167
|
+
(session) => session !== void 0
|
|
31168
|
+
);
|
|
31169
|
+
for (const session of targetSessions) {
|
|
31170
|
+
const handled = await session.handleControl(control);
|
|
31171
|
+
if (handled !== void 0) {
|
|
31172
|
+
return handled;
|
|
31173
|
+
}
|
|
31174
|
+
}
|
|
31175
|
+
return void 0;
|
|
31176
|
+
}
|
|
30791
31177
|
const primaryHandled = await (channelSession?.handleControl(control) ?? Promise.resolve(void 0));
|
|
30792
31178
|
if (primaryHandled !== void 0) {
|
|
30793
31179
|
return primaryHandled;
|
|
@@ -30850,7 +31236,7 @@ function replacementRunnerSummaries(value) {
|
|
|
30850
31236
|
}
|
|
30851
31237
|
function makeRunnerLogger(options) {
|
|
30852
31238
|
return createRunnerLogger(
|
|
30853
|
-
options.logFile ??
|
|
31239
|
+
options.logFile ?? defaultRunnerLogFile(),
|
|
30854
31240
|
options.launchTui ? void 0 : reportRunnerConsoleEvent
|
|
30855
31241
|
);
|
|
30856
31242
|
}
|
|
@@ -31641,6 +32027,7 @@ async function applyControl(codex, kandan, topic, instanceId, options, agentProv
|
|
|
31641
32027
|
case "stop_instance":
|
|
31642
32028
|
case "kill_instance":
|
|
31643
32029
|
case "resolve_port_forward_request":
|
|
32030
|
+
case "update_thread_interaction_access":
|
|
31644
32031
|
case "update_session_settings":
|
|
31645
32032
|
case "set_port_forward_enabled":
|
|
31646
32033
|
case "forward_http_request":
|
|
@@ -54361,7 +54748,7 @@ Codex:
|
|
|
54361
54748
|
Auto-approve detected port-forward candidates for started sessions
|
|
54362
54749
|
--stream-flush-ms <ms> Batch live Codex deltas before Linzumi persistence, default 150
|
|
54363
54750
|
--fast Mark this runner as low-latency/fast in the availability message
|
|
54364
|
-
--log-file <path> JSONL event log path, default
|
|
54751
|
+
--log-file <path> JSONL event log path, default ~/.linzumi/logs/runner-events.jsonl
|
|
54365
54752
|
--allowed-cwd <paths> Extra comma-separated roots where Linzumi may start local Codex sessions
|
|
54366
54753
|
--forward-port <ports> Comma-separated local TCP ports Linzumi may expose as authenticated previews
|
|
54367
54754
|
--code-server-bin <path> Custom development code-server executable. The default editor runtime is downloaded from Linzumi.
|
package/package.json
CHANGED