@junctionpanel/server 0.1.97 → 0.1.99
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/agent-prompt.md +6 -6
- 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 +147 -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,15 @@ 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 === "plan"
|
|
634
|
+
const effectiveMode = input.currentMode === "plan" &&
|
|
635
|
+
input.lastNonPlanMode === "bypassPermissions"
|
|
636
|
+
? "bypassPermissions"
|
|
637
|
+
: input.currentMode;
|
|
608
638
|
if (effectiveMode === "bypassPermissions") {
|
|
609
639
|
return "allow_silent";
|
|
610
640
|
}
|
|
@@ -909,8 +939,7 @@ class TimelineAssembler {
|
|
|
909
939
|
emitNewContent(state) {
|
|
910
940
|
const items = [];
|
|
911
941
|
const nextAssistantText = state.assistantText.slice(state.emittedAssistantLength);
|
|
912
|
-
if (nextAssistantText.length > 0 &&
|
|
913
|
-
nextAssistantText !== INTERRUPT_TOOL_USE_PLACEHOLDER) {
|
|
942
|
+
if (nextAssistantText.length > 0 && !isInterruptPlaceholderText(nextAssistantText)) {
|
|
914
943
|
state.emittedAssistantLength = state.assistantText.length;
|
|
915
944
|
items.push({ type: "assistant_message", text: nextAssistantText });
|
|
916
945
|
}
|
|
@@ -1226,26 +1255,6 @@ class ClaudeAgentSession {
|
|
|
1226
1255
|
updatedInput: input,
|
|
1227
1256
|
};
|
|
1228
1257
|
}
|
|
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
1258
|
const metadata = {};
|
|
1250
1259
|
if (options.toolUseID) {
|
|
1251
1260
|
metadata.toolUseId = options.toolUseID;
|
|
@@ -2026,8 +2035,7 @@ class ClaudeAgentSession {
|
|
|
2026
2035
|
cwd: this.config.cwd,
|
|
2027
2036
|
includePartialMessages: true,
|
|
2028
2037
|
permissionMode: this.currentMode,
|
|
2029
|
-
...(this.currentMode === "bypassPermissions"
|
|
2030
|
-
this.lastNonPlanMode === "bypassPermissions"
|
|
2038
|
+
...(this.currentMode === "bypassPermissions"
|
|
2031
2039
|
? { allowDangerouslySkipPermissions: true }
|
|
2032
2040
|
: {}),
|
|
2033
2041
|
agents: this.defaults?.agents,
|
|
@@ -2472,7 +2480,16 @@ class ClaudeAgentSession {
|
|
|
2472
2480
|
}
|
|
2473
2481
|
const activeRuns = this.runTracker.listActiveRuns();
|
|
2474
2482
|
if (activeRuns.length > 0) {
|
|
2483
|
+
const treatDoneAsInterrupted = this.pendingInterruptPromise !== null;
|
|
2475
2484
|
for (const run of activeRuns) {
|
|
2485
|
+
if (treatDoneAsInterrupted) {
|
|
2486
|
+
this.cancelRun(run, {
|
|
2487
|
+
type: "turn_canceled",
|
|
2488
|
+
provider: "claude",
|
|
2489
|
+
reason: "Interrupted",
|
|
2490
|
+
});
|
|
2491
|
+
continue;
|
|
2492
|
+
}
|
|
2476
2493
|
this.failRun(run, "Claude stream ended before terminal result");
|
|
2477
2494
|
}
|
|
2478
2495
|
}
|
|
@@ -2964,12 +2981,15 @@ class ClaudeAgentSession {
|
|
|
2964
2981
|
break;
|
|
2965
2982
|
}
|
|
2966
2983
|
if (typeof content === "string" && content.length > 0) {
|
|
2967
|
-
|
|
2984
|
+
const normalizedText = extractUserMessageText(content);
|
|
2985
|
+
if (!normalizedText) {
|
|
2986
|
+
break;
|
|
2987
|
+
}
|
|
2968
2988
|
events.push({
|
|
2969
2989
|
type: "timeline",
|
|
2970
2990
|
item: {
|
|
2971
2991
|
type: "user_message",
|
|
2972
|
-
text:
|
|
2992
|
+
text: normalizedText,
|
|
2973
2993
|
...(messageId ? { messageId } : {}),
|
|
2974
2994
|
},
|
|
2975
2995
|
provider: "claude",
|
|
@@ -3263,7 +3283,7 @@ class ClaudeAgentSession {
|
|
|
3263
3283
|
const suppressAssistant = options?.suppressAssistantText ?? false;
|
|
3264
3284
|
const suppressReasoning = options?.suppressReasoning ?? false;
|
|
3265
3285
|
if (typeof content === "string") {
|
|
3266
|
-
if (!content || content
|
|
3286
|
+
if (!content || isInterruptPlaceholderText(content)) {
|
|
3267
3287
|
return [];
|
|
3268
3288
|
}
|
|
3269
3289
|
if (suppressAssistant) {
|
|
@@ -3276,7 +3296,7 @@ class ClaudeAgentSession {
|
|
|
3276
3296
|
switch (block.type) {
|
|
3277
3297
|
case "text":
|
|
3278
3298
|
case "text_delta":
|
|
3279
|
-
if (block.text && block.text
|
|
3299
|
+
if (block.text && !isInterruptPlaceholderText(block.text)) {
|
|
3280
3300
|
if (!suppressAssistant) {
|
|
3281
3301
|
items.push({ type: "assistant_message", text: block.text });
|
|
3282
3302
|
}
|
|
@@ -3718,6 +3738,74 @@ function normalizeHistoryBlocks(content) {
|
|
|
3718
3738
|
}
|
|
3719
3739
|
return null;
|
|
3720
3740
|
}
|
|
3741
|
+
function formatProposedPlanBlock(planText) {
|
|
3742
|
+
return `<proposed_plan>\n${planText}\n</proposed_plan>`;
|
|
3743
|
+
}
|
|
3744
|
+
function extractToolResultToolName(block) {
|
|
3745
|
+
return (readTrimmedString(block.tool_name) ??
|
|
3746
|
+
readTrimmedString(block.name) ??
|
|
3747
|
+
null);
|
|
3748
|
+
}
|
|
3749
|
+
function isPlanApprovalToolResult(block) {
|
|
3750
|
+
const toolName = extractToolResultToolName(block)?.toLowerCase() ?? "";
|
|
3751
|
+
return toolName === "exitplanmode" || toolName === "plan_approval";
|
|
3752
|
+
}
|
|
3753
|
+
function extractApprovedPlanFromText(text) {
|
|
3754
|
+
const match = text.match(/## Approved Plan(?: \(edited by user\))?:\s*([\s\S]*)$/i);
|
|
3755
|
+
const planText = match?.[1]?.trim();
|
|
3756
|
+
return planText && planText.length > 0 ? planText : null;
|
|
3757
|
+
}
|
|
3758
|
+
function extractApprovedPlanFromPayload(value) {
|
|
3759
|
+
if (!isMetadata(value)) {
|
|
3760
|
+
return null;
|
|
3761
|
+
}
|
|
3762
|
+
return readTrimmedString(value.plan) ?? null;
|
|
3763
|
+
}
|
|
3764
|
+
function extractApprovedPlanFromHistoryBlock(block) {
|
|
3765
|
+
const type = readTrimmedString(block.type)?.toLowerCase() ?? "";
|
|
3766
|
+
if (!type.includes("tool_result")) {
|
|
3767
|
+
return null;
|
|
3768
|
+
}
|
|
3769
|
+
if (!isPlanApprovalToolResult(block)) {
|
|
3770
|
+
return null;
|
|
3771
|
+
}
|
|
3772
|
+
const planFromPayload = extractApprovedPlanFromPayload(block.toolUseResult) ??
|
|
3773
|
+
extractApprovedPlanFromPayload(block.tool_use_result);
|
|
3774
|
+
if (planFromPayload) {
|
|
3775
|
+
return planFromPayload;
|
|
3776
|
+
}
|
|
3777
|
+
const contentText = coerceToolResultContentToString(block.content).trim();
|
|
3778
|
+
if (contentText.length === 0) {
|
|
3779
|
+
return null;
|
|
3780
|
+
}
|
|
3781
|
+
return extractApprovedPlanFromText(contentText);
|
|
3782
|
+
}
|
|
3783
|
+
function extractApprovedPlanFromHistoryEntry(entry) {
|
|
3784
|
+
if (entry?.type !== "user") {
|
|
3785
|
+
return null;
|
|
3786
|
+
}
|
|
3787
|
+
const content = entry?.message?.content;
|
|
3788
|
+
const normalizedBlocks = normalizeHistoryBlocks(content);
|
|
3789
|
+
if (!normalizedBlocks) {
|
|
3790
|
+
return null;
|
|
3791
|
+
}
|
|
3792
|
+
for (const block of normalizedBlocks) {
|
|
3793
|
+
const planText = extractApprovedPlanFromHistoryBlock(block);
|
|
3794
|
+
if (planText) {
|
|
3795
|
+
return planText;
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
return null;
|
|
3799
|
+
}
|
|
3800
|
+
const APPROVED_PLAN_RESOLUTION_MARKER = "Plan approved. Execute it now.";
|
|
3801
|
+
function buildApprovedPlanTimelineItem(planText) {
|
|
3802
|
+
// Append a resolved marker so replayed approved plans stay extractable as cards
|
|
3803
|
+
// without being mistaken for an unresolved timeline-only plan review.
|
|
3804
|
+
return {
|
|
3805
|
+
type: "assistant_message",
|
|
3806
|
+
text: `${formatProposedPlanBlock(planText)}\n\n${APPROVED_PLAN_RESOLUTION_MARKER}`,
|
|
3807
|
+
};
|
|
3808
|
+
}
|
|
3721
3809
|
export function convertClaudeHistoryEntry(entry, mapBlocks) {
|
|
3722
3810
|
if (entry.type === "system" && entry.subtype === "compact_boundary") {
|
|
3723
3811
|
const compactMetadata = readCompactionMetadata(entry);
|
|
@@ -3762,6 +3850,7 @@ export function convertClaudeHistoryEntry(entry, mapBlocks) {
|
|
|
3762
3850
|
return [taskNotificationItem];
|
|
3763
3851
|
}
|
|
3764
3852
|
}
|
|
3853
|
+
const approvedPlan = extractApprovedPlanFromHistoryEntry(entry);
|
|
3765
3854
|
const timeline = [];
|
|
3766
3855
|
if (entry.type === "user") {
|
|
3767
3856
|
const text = extractUserMessageText(content);
|
|
@@ -3777,7 +3866,17 @@ export function convertClaudeHistoryEntry(entry, mapBlocks) {
|
|
|
3777
3866
|
const mapped = mapBlocks(normalizedBlocks);
|
|
3778
3867
|
if (entry.type === "user") {
|
|
3779
3868
|
const toolItems = mapped.filter((item) => item.type === "tool_call");
|
|
3780
|
-
|
|
3869
|
+
const nextTimeline = timeline.length
|
|
3870
|
+
? [
|
|
3871
|
+
...timeline,
|
|
3872
|
+
...(approvedPlan ? [buildApprovedPlanTimelineItem(approvedPlan)] : []),
|
|
3873
|
+
...toolItems,
|
|
3874
|
+
]
|
|
3875
|
+
: [
|
|
3876
|
+
...(approvedPlan ? [buildApprovedPlanTimelineItem(approvedPlan)] : []),
|
|
3877
|
+
...toolItems,
|
|
3878
|
+
];
|
|
3879
|
+
return nextTimeline;
|
|
3781
3880
|
}
|
|
3782
3881
|
return mapped;
|
|
3783
3882
|
}
|
|
@@ -3962,22 +4061,26 @@ async function parseClaudeSessionDescriptor(filePath, mtime) {
|
|
|
3962
4061
|
timeline,
|
|
3963
4062
|
};
|
|
3964
4063
|
}
|
|
3965
|
-
function extractClaudeUserText(message) {
|
|
4064
|
+
export function extractClaudeUserText(message) {
|
|
3966
4065
|
if (!message) {
|
|
3967
4066
|
return null;
|
|
3968
4067
|
}
|
|
3969
4068
|
if (typeof message.content === "string") {
|
|
3970
|
-
|
|
4069
|
+
const normalized = message.content.trim();
|
|
4070
|
+
if (normalized.length === 0 || isHiddenClaudeLocalCommandText(normalized)) {
|
|
4071
|
+
return null;
|
|
4072
|
+
}
|
|
4073
|
+
return normalized;
|
|
3971
4074
|
}
|
|
3972
4075
|
if (typeof message.text === "string") {
|
|
3973
|
-
|
|
4076
|
+
const normalized = message.text.trim();
|
|
4077
|
+
if (normalized.length === 0 || isHiddenClaudeLocalCommandText(normalized)) {
|
|
4078
|
+
return null;
|
|
4079
|
+
}
|
|
4080
|
+
return normalized;
|
|
3974
4081
|
}
|
|
3975
4082
|
if (Array.isArray(message.content)) {
|
|
3976
|
-
|
|
3977
|
-
if (block && typeof block.text === "string") {
|
|
3978
|
-
return block.text.trim();
|
|
3979
|
-
}
|
|
3980
|
-
}
|
|
4083
|
+
return extractUserMessageText(message.content);
|
|
3981
4084
|
}
|
|
3982
4085
|
return null;
|
|
3983
4086
|
}
|