@danypops/papyrus 0.27.1 → 0.27.2
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.
|
@@ -1073,6 +1073,24 @@ async function askViaDialogs(
|
|
|
1073
1073
|
return createSelectionResponse([selected], comment);
|
|
1074
1074
|
}
|
|
1075
1075
|
|
|
1076
|
+
/**
|
|
1077
|
+
* Tracks whether a live ask is genuinely mid-flight, blocked on the human. `ExtensionContext.isIdle()`
|
|
1078
|
+
* means "not streaming a model response" -- it reads true while a slow, human-blocking tool call
|
|
1079
|
+
* like this one is still pending, since the model already finished emitting the tool_call and
|
|
1080
|
+
* is not itself generating anything. Left unguarded, that lets the active-task continuation
|
|
1081
|
+
* driver (extension/src/index.ts's driveActiveTasks, on agent_settled) queue a "continue the
|
|
1082
|
+
* active task" nudge as a `deliverAs: "nextTurn"` message while this exact live ask is still
|
|
1083
|
+
* awaiting an answer -- starting a second, concurrent turn that reasons about the very Discussion
|
|
1084
|
+
* this call is already resolving, independently of it. A live-observed bug (two pickers for the
|
|
1085
|
+
* same question, one orphaned and later auto-resolving with fabricated "defer" text) traced back
|
|
1086
|
+
* to exactly this race. driveActiveTasks checks isLiveAskPending() and skips queuing while true.
|
|
1087
|
+
*/
|
|
1088
|
+
let livePendingCount = 0;
|
|
1089
|
+
|
|
1090
|
+
export function isLiveAskPending(): boolean {
|
|
1091
|
+
return livePendingCount > 0;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1076
1094
|
/**
|
|
1077
1095
|
* Discuss's live:true synchronous ask -- interactive AskComponent when a real TUI is available,
|
|
1078
1096
|
* dialog fallback (ctx.ui.select/input) in RPC/headless mode, no-op undefined without any
|
|
@@ -1092,7 +1110,24 @@ export async function askQuestion(ctx: ExtensionContext, params: AskQuestionPara
|
|
|
1092
1110
|
const normalizedContext = params.context?.trim() || undefined;
|
|
1093
1111
|
|
|
1094
1112
|
params.onUpdate?.({ content: [{ type: "text", text: "Waiting for human input..." }], details: undefined });
|
|
1113
|
+
livePendingCount += 1;
|
|
1114
|
+
try {
|
|
1115
|
+
return await askQuestionBlocking(ctx, params, options, allowMultiple, allowFreeform, allowComment, displayMode, normalizedContext);
|
|
1116
|
+
} finally {
|
|
1117
|
+
livePendingCount -= 1;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1095
1120
|
|
|
1121
|
+
async function askQuestionBlocking(
|
|
1122
|
+
ctx: ExtensionContext,
|
|
1123
|
+
params: AskQuestionParams,
|
|
1124
|
+
options: AskOption[],
|
|
1125
|
+
allowMultiple: boolean,
|
|
1126
|
+
allowFreeform: boolean,
|
|
1127
|
+
allowComment: boolean,
|
|
1128
|
+
displayMode: AskDisplayMode,
|
|
1129
|
+
normalizedContext: string | undefined,
|
|
1130
|
+
): Promise<AskAnswer | undefined> {
|
|
1096
1131
|
if (options.length === 0) {
|
|
1097
1132
|
const prompt = normalizedContext ? `${params.question}\n\nContext:\n${normalizedContext}` : params.question;
|
|
1098
1133
|
const answer = await ctx.ui.input(prompt, "Type your answer...", params.timeout ? { timeout: params.timeout } : undefined);
|
package/extension/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type { GateResult } from "../../src/domain/gate.ts";
|
|
|
22
22
|
import { formatMetadata } from "./artifact-format.ts";
|
|
23
23
|
import { callService } from "./service-client.ts";
|
|
24
24
|
import { registerDomainTools } from "./domain-tools.ts";
|
|
25
|
+
import { isLiveAskPending } from "./discuss-ask-view.ts";
|
|
25
26
|
import { registerPlaybookBridge } from "./playbook-bridge.ts";
|
|
26
27
|
import type { TaskGraph, TaskStatus } from "../../src/task-service.ts";
|
|
27
28
|
import { ActiveTaskContinuation, automaticPauseReason, shouldResumeFocusOnHumanInput, type ActiveTaskMarker } from "./active-task-continuation.ts";
|
|
@@ -202,6 +203,11 @@ export default async function (pi: ExtensionAPI) {
|
|
|
202
203
|
|
|
203
204
|
const driveActiveTasks = async (ctx: ExtensionContext): Promise<void> => {
|
|
204
205
|
if (ctx.mode !== "tui" && ctx.mode !== "rpc") return;
|
|
206
|
+
// ctx.isIdle() means "not streaming a model response" -- it reads true while a live discuss
|
|
207
|
+
// ask is still genuinely pending, blocked on the human. Queuing a "continue the active task"
|
|
208
|
+
// nudge here would start a second, concurrent turn reasoning about the very Discussion this
|
|
209
|
+
// live ask is already resolving. See discuss-ask-view.ts's isLiveAskPending() doc comment.
|
|
210
|
+
if (isLiveAskPending()) return;
|
|
205
211
|
try {
|
|
206
212
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
207
213
|
const active = await callService<Record<string, unknown>, ActiveTaskMarker | null>("tasks.active", { project_root: ctx.cwd, session_id: sessionId });
|
package/package.json
CHANGED