@junctionpanel/server 0.1.97 → 0.1.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/server/agent/agent-manager.d.ts +2 -0
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +64 -42
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts +1 -0
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +144 -44
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/session.js +3 -3
- package/dist/server/server/session.js.map +1 -1
- package/package.json +2 -2
|
@@ -216,8 +216,14 @@ const REWIND_COMMAND = {
|
|
|
216
216
|
description: "Rewind tracked files to a previous user message",
|
|
217
217
|
argumentHint: "[user_message_uuid]",
|
|
218
218
|
};
|
|
219
|
-
const
|
|
219
|
+
const INTERRUPT_PLACEHOLDERS = new Set([
|
|
220
|
+
"[Request interrupted by user]",
|
|
221
|
+
"[Request interrupted by user for tool use]",
|
|
222
|
+
]);
|
|
220
223
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
224
|
+
function isInterruptPlaceholderText(text) {
|
|
225
|
+
return INTERRUPT_PLACEHOLDERS.has(text.trim());
|
|
226
|
+
}
|
|
221
227
|
function resolveClaudeBinary() {
|
|
222
228
|
const claudePath = resolveCommandPathWithFallback("claude", {
|
|
223
229
|
env: process.env,
|
|
@@ -387,10 +393,30 @@ function coerceToolResultContentToString(content) {
|
|
|
387
393
|
}
|
|
388
394
|
return deterministicStringify(content);
|
|
389
395
|
}
|
|
396
|
+
const CLAUDE_LOCAL_COMMAND_ENVELOPE_PATTERN = /^<local-command-(stdout|stderr)>\s*([\s\S]*?)\s*<\/local-command-\1>$/i;
|
|
397
|
+
const CLAUDE_MODEL_SWITCH_ACK_PATTERN = /^set model to\b/i;
|
|
398
|
+
function unwrapClaudeLocalCommandEnvelope(content) {
|
|
399
|
+
const match = CLAUDE_LOCAL_COMMAND_ENVELOPE_PATTERN.exec(content.trim());
|
|
400
|
+
if (!match) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
const innerText = match[2]?.trim() ?? "";
|
|
404
|
+
return innerText.length > 0 ? innerText : null;
|
|
405
|
+
}
|
|
406
|
+
function isHiddenClaudeLocalCommandText(content) {
|
|
407
|
+
const innerText = unwrapClaudeLocalCommandEnvelope(content);
|
|
408
|
+
if (!innerText) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
return CLAUDE_MODEL_SWITCH_ACK_PATTERN.test(innerText);
|
|
412
|
+
}
|
|
390
413
|
export function extractUserMessageText(content) {
|
|
391
414
|
if (typeof content === "string") {
|
|
392
415
|
const normalized = content.trim();
|
|
393
|
-
|
|
416
|
+
if (normalized.length === 0 || isHiddenClaudeLocalCommandText(normalized)) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
return normalized;
|
|
394
420
|
}
|
|
395
421
|
if (!Array.isArray(content)) {
|
|
396
422
|
return null;
|
|
@@ -414,7 +440,10 @@ export function extractUserMessageText(content) {
|
|
|
414
440
|
return null;
|
|
415
441
|
}
|
|
416
442
|
const combined = parts.join("\n\n").trim();
|
|
417
|
-
|
|
443
|
+
if (combined.length === 0 || isHiddenClaudeLocalCommandText(combined)) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
return combined;
|
|
418
447
|
}
|
|
419
448
|
const MAX_SUB_AGENT_LOG_ENTRIES = 200;
|
|
420
449
|
const MAX_SUB_AGENT_SUMMARY_CHARS = 160;
|
|
@@ -597,14 +626,12 @@ function resolveClaudePermissionDecision(input) {
|
|
|
597
626
|
return "prompt";
|
|
598
627
|
}
|
|
599
628
|
if (input.kind === "plan") {
|
|
600
|
-
return
|
|
601
|
-
? "allow_plan_transition"
|
|
602
|
-
: "prompt";
|
|
629
|
+
return "prompt";
|
|
603
630
|
}
|
|
604
631
|
if (input.kind !== "tool") {
|
|
605
632
|
return "prompt";
|
|
606
633
|
}
|
|
607
|
-
const effectiveMode = input.currentMode
|
|
634
|
+
const effectiveMode = input.currentMode;
|
|
608
635
|
if (effectiveMode === "bypassPermissions") {
|
|
609
636
|
return "allow_silent";
|
|
610
637
|
}
|
|
@@ -909,8 +936,7 @@ class TimelineAssembler {
|
|
|
909
936
|
emitNewContent(state) {
|
|
910
937
|
const items = [];
|
|
911
938
|
const nextAssistantText = state.assistantText.slice(state.emittedAssistantLength);
|
|
912
|
-
if (nextAssistantText.length > 0 &&
|
|
913
|
-
nextAssistantText !== INTERRUPT_TOOL_USE_PLACEHOLDER) {
|
|
939
|
+
if (nextAssistantText.length > 0 && !isInterruptPlaceholderText(nextAssistantText)) {
|
|
914
940
|
state.emittedAssistantLength = state.assistantText.length;
|
|
915
941
|
items.push({ type: "assistant_message", text: nextAssistantText });
|
|
916
942
|
}
|
|
@@ -1226,26 +1252,6 @@ class ClaudeAgentSession {
|
|
|
1226
1252
|
updatedInput: input,
|
|
1227
1253
|
};
|
|
1228
1254
|
}
|
|
1229
|
-
if (decision === "allow_plan_transition") {
|
|
1230
|
-
this.currentMode = "bypassPermissions";
|
|
1231
|
-
this.cachedRuntimeInfo = null;
|
|
1232
|
-
this.pushToolCall(mapClaudeCompletedToolCall({
|
|
1233
|
-
name: "plan_approval",
|
|
1234
|
-
callId: options.toolUseID ?? requestId,
|
|
1235
|
-
input,
|
|
1236
|
-
output: { approved: true, automatic: true },
|
|
1237
|
-
}));
|
|
1238
|
-
this.pushEvent({
|
|
1239
|
-
type: "permission_resolved",
|
|
1240
|
-
provider: "claude",
|
|
1241
|
-
requestId,
|
|
1242
|
-
resolution: { behavior: "allow" },
|
|
1243
|
-
});
|
|
1244
|
-
return {
|
|
1245
|
-
behavior: "allow",
|
|
1246
|
-
updatedInput: input,
|
|
1247
|
-
};
|
|
1248
|
-
}
|
|
1249
1255
|
const metadata = {};
|
|
1250
1256
|
if (options.toolUseID) {
|
|
1251
1257
|
metadata.toolUseId = options.toolUseID;
|
|
@@ -2026,8 +2032,7 @@ class ClaudeAgentSession {
|
|
|
2026
2032
|
cwd: this.config.cwd,
|
|
2027
2033
|
includePartialMessages: true,
|
|
2028
2034
|
permissionMode: this.currentMode,
|
|
2029
|
-
...(this.currentMode === "bypassPermissions"
|
|
2030
|
-
this.lastNonPlanMode === "bypassPermissions"
|
|
2035
|
+
...(this.currentMode === "bypassPermissions"
|
|
2031
2036
|
? { allowDangerouslySkipPermissions: true }
|
|
2032
2037
|
: {}),
|
|
2033
2038
|
agents: this.defaults?.agents,
|
|
@@ -2472,7 +2477,16 @@ class ClaudeAgentSession {
|
|
|
2472
2477
|
}
|
|
2473
2478
|
const activeRuns = this.runTracker.listActiveRuns();
|
|
2474
2479
|
if (activeRuns.length > 0) {
|
|
2480
|
+
const treatDoneAsInterrupted = this.pendingInterruptPromise !== null;
|
|
2475
2481
|
for (const run of activeRuns) {
|
|
2482
|
+
if (treatDoneAsInterrupted) {
|
|
2483
|
+
this.cancelRun(run, {
|
|
2484
|
+
type: "turn_canceled",
|
|
2485
|
+
provider: "claude",
|
|
2486
|
+
reason: "Interrupted",
|
|
2487
|
+
});
|
|
2488
|
+
continue;
|
|
2489
|
+
}
|
|
2476
2490
|
this.failRun(run, "Claude stream ended before terminal result");
|
|
2477
2491
|
}
|
|
2478
2492
|
}
|
|
@@ -2964,12 +2978,15 @@ class ClaudeAgentSession {
|
|
|
2964
2978
|
break;
|
|
2965
2979
|
}
|
|
2966
2980
|
if (typeof content === "string" && content.length > 0) {
|
|
2967
|
-
|
|
2981
|
+
const normalizedText = extractUserMessageText(content);
|
|
2982
|
+
if (!normalizedText) {
|
|
2983
|
+
break;
|
|
2984
|
+
}
|
|
2968
2985
|
events.push({
|
|
2969
2986
|
type: "timeline",
|
|
2970
2987
|
item: {
|
|
2971
2988
|
type: "user_message",
|
|
2972
|
-
text:
|
|
2989
|
+
text: normalizedText,
|
|
2973
2990
|
...(messageId ? { messageId } : {}),
|
|
2974
2991
|
},
|
|
2975
2992
|
provider: "claude",
|
|
@@ -3263,7 +3280,7 @@ class ClaudeAgentSession {
|
|
|
3263
3280
|
const suppressAssistant = options?.suppressAssistantText ?? false;
|
|
3264
3281
|
const suppressReasoning = options?.suppressReasoning ?? false;
|
|
3265
3282
|
if (typeof content === "string") {
|
|
3266
|
-
if (!content || content
|
|
3283
|
+
if (!content || isInterruptPlaceholderText(content)) {
|
|
3267
3284
|
return [];
|
|
3268
3285
|
}
|
|
3269
3286
|
if (suppressAssistant) {
|
|
@@ -3276,7 +3293,7 @@ class ClaudeAgentSession {
|
|
|
3276
3293
|
switch (block.type) {
|
|
3277
3294
|
case "text":
|
|
3278
3295
|
case "text_delta":
|
|
3279
|
-
if (block.text && block.text
|
|
3296
|
+
if (block.text && !isInterruptPlaceholderText(block.text)) {
|
|
3280
3297
|
if (!suppressAssistant) {
|
|
3281
3298
|
items.push({ type: "assistant_message", text: block.text });
|
|
3282
3299
|
}
|
|
@@ -3718,6 +3735,74 @@ function normalizeHistoryBlocks(content) {
|
|
|
3718
3735
|
}
|
|
3719
3736
|
return null;
|
|
3720
3737
|
}
|
|
3738
|
+
function formatProposedPlanBlock(planText) {
|
|
3739
|
+
return `<proposed_plan>\n${planText}\n</proposed_plan>`;
|
|
3740
|
+
}
|
|
3741
|
+
function extractToolResultToolName(block) {
|
|
3742
|
+
return (readTrimmedString(block.tool_name) ??
|
|
3743
|
+
readTrimmedString(block.name) ??
|
|
3744
|
+
null);
|
|
3745
|
+
}
|
|
3746
|
+
function isPlanApprovalToolResult(block) {
|
|
3747
|
+
const toolName = extractToolResultToolName(block)?.toLowerCase() ?? "";
|
|
3748
|
+
return toolName === "exitplanmode" || toolName === "plan_approval";
|
|
3749
|
+
}
|
|
3750
|
+
function extractApprovedPlanFromText(text) {
|
|
3751
|
+
const match = text.match(/## Approved Plan(?: \(edited by user\))?:\s*([\s\S]*)$/i);
|
|
3752
|
+
const planText = match?.[1]?.trim();
|
|
3753
|
+
return planText && planText.length > 0 ? planText : null;
|
|
3754
|
+
}
|
|
3755
|
+
function extractApprovedPlanFromPayload(value) {
|
|
3756
|
+
if (!isMetadata(value)) {
|
|
3757
|
+
return null;
|
|
3758
|
+
}
|
|
3759
|
+
return readTrimmedString(value.plan) ?? null;
|
|
3760
|
+
}
|
|
3761
|
+
function extractApprovedPlanFromHistoryBlock(block) {
|
|
3762
|
+
const type = readTrimmedString(block.type)?.toLowerCase() ?? "";
|
|
3763
|
+
if (!type.includes("tool_result")) {
|
|
3764
|
+
return null;
|
|
3765
|
+
}
|
|
3766
|
+
if (!isPlanApprovalToolResult(block)) {
|
|
3767
|
+
return null;
|
|
3768
|
+
}
|
|
3769
|
+
const planFromPayload = extractApprovedPlanFromPayload(block.toolUseResult) ??
|
|
3770
|
+
extractApprovedPlanFromPayload(block.tool_use_result);
|
|
3771
|
+
if (planFromPayload) {
|
|
3772
|
+
return planFromPayload;
|
|
3773
|
+
}
|
|
3774
|
+
const contentText = coerceToolResultContentToString(block.content).trim();
|
|
3775
|
+
if (contentText.length === 0) {
|
|
3776
|
+
return null;
|
|
3777
|
+
}
|
|
3778
|
+
return extractApprovedPlanFromText(contentText);
|
|
3779
|
+
}
|
|
3780
|
+
function extractApprovedPlanFromHistoryEntry(entry) {
|
|
3781
|
+
if (entry?.type !== "user") {
|
|
3782
|
+
return null;
|
|
3783
|
+
}
|
|
3784
|
+
const content = entry?.message?.content;
|
|
3785
|
+
const normalizedBlocks = normalizeHistoryBlocks(content);
|
|
3786
|
+
if (!normalizedBlocks) {
|
|
3787
|
+
return null;
|
|
3788
|
+
}
|
|
3789
|
+
for (const block of normalizedBlocks) {
|
|
3790
|
+
const planText = extractApprovedPlanFromHistoryBlock(block);
|
|
3791
|
+
if (planText) {
|
|
3792
|
+
return planText;
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
return null;
|
|
3796
|
+
}
|
|
3797
|
+
const APPROVED_PLAN_RESOLUTION_MARKER = "Plan approved. Execute it now.";
|
|
3798
|
+
function buildApprovedPlanTimelineItem(planText) {
|
|
3799
|
+
// Append a resolved marker so replayed approved plans stay extractable as cards
|
|
3800
|
+
// without being mistaken for an unresolved timeline-only plan review.
|
|
3801
|
+
return {
|
|
3802
|
+
type: "assistant_message",
|
|
3803
|
+
text: `${formatProposedPlanBlock(planText)}\n\n${APPROVED_PLAN_RESOLUTION_MARKER}`,
|
|
3804
|
+
};
|
|
3805
|
+
}
|
|
3721
3806
|
export function convertClaudeHistoryEntry(entry, mapBlocks) {
|
|
3722
3807
|
if (entry.type === "system" && entry.subtype === "compact_boundary") {
|
|
3723
3808
|
const compactMetadata = readCompactionMetadata(entry);
|
|
@@ -3762,6 +3847,7 @@ export function convertClaudeHistoryEntry(entry, mapBlocks) {
|
|
|
3762
3847
|
return [taskNotificationItem];
|
|
3763
3848
|
}
|
|
3764
3849
|
}
|
|
3850
|
+
const approvedPlan = extractApprovedPlanFromHistoryEntry(entry);
|
|
3765
3851
|
const timeline = [];
|
|
3766
3852
|
if (entry.type === "user") {
|
|
3767
3853
|
const text = extractUserMessageText(content);
|
|
@@ -3777,7 +3863,17 @@ export function convertClaudeHistoryEntry(entry, mapBlocks) {
|
|
|
3777
3863
|
const mapped = mapBlocks(normalizedBlocks);
|
|
3778
3864
|
if (entry.type === "user") {
|
|
3779
3865
|
const toolItems = mapped.filter((item) => item.type === "tool_call");
|
|
3780
|
-
|
|
3866
|
+
const nextTimeline = timeline.length
|
|
3867
|
+
? [
|
|
3868
|
+
...timeline,
|
|
3869
|
+
...(approvedPlan ? [buildApprovedPlanTimelineItem(approvedPlan)] : []),
|
|
3870
|
+
...toolItems,
|
|
3871
|
+
]
|
|
3872
|
+
: [
|
|
3873
|
+
...(approvedPlan ? [buildApprovedPlanTimelineItem(approvedPlan)] : []),
|
|
3874
|
+
...toolItems,
|
|
3875
|
+
];
|
|
3876
|
+
return nextTimeline;
|
|
3781
3877
|
}
|
|
3782
3878
|
return mapped;
|
|
3783
3879
|
}
|
|
@@ -3962,22 +4058,26 @@ async function parseClaudeSessionDescriptor(filePath, mtime) {
|
|
|
3962
4058
|
timeline,
|
|
3963
4059
|
};
|
|
3964
4060
|
}
|
|
3965
|
-
function extractClaudeUserText(message) {
|
|
4061
|
+
export function extractClaudeUserText(message) {
|
|
3966
4062
|
if (!message) {
|
|
3967
4063
|
return null;
|
|
3968
4064
|
}
|
|
3969
4065
|
if (typeof message.content === "string") {
|
|
3970
|
-
|
|
4066
|
+
const normalized = message.content.trim();
|
|
4067
|
+
if (normalized.length === 0 || isHiddenClaudeLocalCommandText(normalized)) {
|
|
4068
|
+
return null;
|
|
4069
|
+
}
|
|
4070
|
+
return normalized;
|
|
3971
4071
|
}
|
|
3972
4072
|
if (typeof message.text === "string") {
|
|
3973
|
-
|
|
4073
|
+
const normalized = message.text.trim();
|
|
4074
|
+
if (normalized.length === 0 || isHiddenClaudeLocalCommandText(normalized)) {
|
|
4075
|
+
return null;
|
|
4076
|
+
}
|
|
4077
|
+
return normalized;
|
|
3974
4078
|
}
|
|
3975
4079
|
if (Array.isArray(message.content)) {
|
|
3976
|
-
|
|
3977
|
-
if (block && typeof block.text === "string") {
|
|
3978
|
-
return block.text.trim();
|
|
3979
|
-
}
|
|
3980
|
-
}
|
|
4080
|
+
return extractUserMessageText(message.content);
|
|
3981
4081
|
}
|
|
3982
4082
|
return null;
|
|
3983
4083
|
}
|