fix(ext): !stop getActiveSessionId stale primitive — use step-probe getter #task-410
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"name": "gravity-bridge",
|
||||
"displayName": "Gravity Bridge",
|
||||
"description": "Antigravity ↔ Discord 브리지 연동 확장",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"publisher": "variet",
|
||||
"engines": {
|
||||
"vscode": "^1.100.0"
|
||||
|
||||
@@ -16,7 +16,7 @@ import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as cp from 'child_process';
|
||||
import { WSBridgeClient, WSResponseData, WSCommandData } from './ws-client';
|
||||
import { initStepProbe, BridgeContext, writePendingApproval, tryApprovalStrategies, writeRegistration, getApprovalContext, resetPendingState, handleDiffReviewResponse } from './step-probe';
|
||||
import { initStepProbe, BridgeContext, writePendingApproval, tryApprovalStrategies, writeRegistration, getApprovalContext, resetPendingState, handleDiffReviewResponse, getActiveSessionId as getStepProbeSessionId } from './step-probe';
|
||||
import { startHttpBridge, getDeterministicPort, HttpBridgeContext } from './http-bridge';
|
||||
import { setupApprovalObserver } from './html-patcher';
|
||||
import { watchCommandsDir, handleWSCommand, disposeCommandsWatcher, CommandHandlerContext } from './command-handler';
|
||||
@@ -430,7 +430,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
bridgePath, projectName, sdk, ls: sdk?.ls, autoApproveEnabled, logToFile,
|
||||
onAutoApproveChanged: (enabled: boolean) => { autoApproveEnabled = enabled; },
|
||||
recentDiscordSentTexts,
|
||||
getActiveSessionId: () => activeSessionId,
|
||||
getActiveSessionId: () => getStepProbeSessionId(),
|
||||
}, data);
|
||||
},
|
||||
onInstanceUpdate: (count, instances) => {
|
||||
@@ -560,7 +560,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
bridgePath, projectName, sdk, ls: sdk?.ls, autoApproveEnabled, logToFile,
|
||||
onAutoApproveChanged: (enabled: boolean) => { autoApproveEnabled = enabled; },
|
||||
recentDiscordSentTexts,
|
||||
getActiveSessionId: () => activeSessionId,
|
||||
getActiveSessionId: () => getStepProbeSessionId(),
|
||||
});
|
||||
|
||||
// Response watcher is now initialized by initStepProbe() above
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface BridgeContext {
|
||||
diffReviewMetadata: Map<string, { edit_step_indices: number[]; modified_files: string[] }>;
|
||||
recentDiscordSentTexts: Map<string, number>;
|
||||
writeChatSnapshot: (text: string) => void;
|
||||
writeChatSnapshotWithFiles: (text: string, files: Array<{name: string, content: string}>) => void;
|
||||
writeChatSnapshotWithFiles: (text: string, files: Array<{ name: string, content: string }>) => void;
|
||||
}
|
||||
|
||||
let ctx: BridgeContext;
|
||||
@@ -52,6 +52,18 @@ export function getApprovalContext(): { sessionId: string; stepIndex: number } {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active session ID tracked by step-probe polling.
|
||||
* Used by command-handler (via extension.ts) for !stop → CancelCascadeInvocation.
|
||||
*
|
||||
* CRITICAL: extension.ts must use this getter instead of its own module-level
|
||||
* `activeSessionId` variable, which is a stale primitive copy that never updates
|
||||
* when step-probe discovers new sessions.
|
||||
*/
|
||||
export function getActiveSessionId(): string {
|
||||
return ctx?.activeSessionId || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset pending state after successful approval.
|
||||
* Called after WS response triggers approval in extension.ts.
|
||||
@@ -731,7 +743,7 @@ function setupMonitor() {
|
||||
: fileContent;
|
||||
// Write as snapshot with attached_files for bot to send as Discord file
|
||||
ctx.writeChatSnapshotWithFiles(
|
||||
`📎 **문서: ${fileName}** (${Math.round(fileContent.length/1024)}KB)`,
|
||||
`📎 **문서: ${fileName}** (${Math.round(fileContent.length / 1024)}KB)`,
|
||||
[{ name: fileName, content: truncatedContent }]
|
||||
);
|
||||
ctx.logToFile(`[NOTIFY-STEP] relayed artifact: ${fileName} (${fileContent.length} chars)`);
|
||||
@@ -797,14 +809,14 @@ function setupMonitor() {
|
||||
const sentAt = ctx.recentDiscordSentTexts.get(trimmed);
|
||||
if (sentAt && (Date.now() - sentAt) < 60_000) {
|
||||
ctx.recentDiscordSentTexts.delete(trimmed);
|
||||
ctx.logToFile(`[USER-MSG] skipped echo relay (Discord origin, ${Math.round((Date.now()-sentAt)/1000)}s ago)`);
|
||||
ctx.logToFile(`[USER-MSG] skipped echo relay (Discord origin, ${Math.round((Date.now() - sentAt) / 1000)}s ago)`);
|
||||
} else if (umText.length > 2) {
|
||||
// Content-based dedup: AG can create multiple USER_INPUT steps for the same message
|
||||
// (e.g. comment-while-working feature). Skip if same text relayed within 30s.
|
||||
const dedupKey = `user_msg:${trimmed}`;
|
||||
const lastRelayed = lastSnapshotText.get(dedupKey);
|
||||
if (lastRelayed && (Date.now() - Number(lastRelayed)) < 30_000) {
|
||||
ctx.logToFile(`[USER-MSG] skipped duplicate relay (same text ${Math.round((Date.now() - Number(lastRelayed))/1000)}s ago)`);
|
||||
ctx.logToFile(`[USER-MSG] skipped duplicate relay (same text ${Math.round((Date.now() - Number(lastRelayed)) / 1000)}s ago)`);
|
||||
} else {
|
||||
lastSnapshotText.set(dedupKey, String(Date.now()));
|
||||
const truncated = umText.length > 800
|
||||
@@ -858,7 +870,7 @@ function setupMonitor() {
|
||||
}
|
||||
// Log first time to capture actual field names
|
||||
if (!textContent) {
|
||||
ctx.logToFile(`[RESPONSE-CAPTURE] plannerResponse keys: [${Object.keys(pr).join(',')}] values(100): ${JSON.stringify(pr).substring(0,200)}`);
|
||||
ctx.logToFile(`[RESPONSE-CAPTURE] plannerResponse keys: [${Object.keys(pr).join(',')}] values(100): ${JSON.stringify(pr).substring(0, 200)}`);
|
||||
}
|
||||
}
|
||||
// Extract from ephemeralMessage field
|
||||
@@ -1252,7 +1264,7 @@ async function processResponseFile(filePath: string) {
|
||||
|
||||
|
||||
/** Write a pending approval file matching Bot's ApprovalRequest dataclass. */
|
||||
export function writePendingApproval(data: { conversation_id: string; command: string; description: string; step_type?: string; step_index?: number; source?: string; buttons?: Array<{text: string; index: number}>; modified_files?: string[]; edit_step_indices?: number[] }) {
|
||||
export function writePendingApproval(data: { conversation_id: string; command: string; description: string; step_type?: string; step_index?: number; source?: string; buttons?: Array<{ text: string; index: number }>; modified_files?: string[]; edit_step_indices?: number[] }) {
|
||||
try {
|
||||
const pendingDir = path.join(ctx.bridgePath, 'pending');
|
||||
if (!fs.existsSync(pendingDir)) { fs.mkdirSync(pendingDir, { recursive: true }); }
|
||||
@@ -1459,7 +1471,7 @@ export async function tryApprovalStrategies(approved: boolean, sessionId: string
|
||||
}
|
||||
// Direct LS RPC with correct method name
|
||||
try {
|
||||
ctx.logToFile(`[APPROVAL-CODE-EDIT] acknowledgeCodeActionStep(cascadeId=${sessionId.substring(0,8)}, accept=${approved}, stepIndices=[${effectiveStepIndex}])`);
|
||||
ctx.logToFile(`[APPROVAL-CODE-EDIT] acknowledgeCodeActionStep(cascadeId=${sessionId.substring(0, 8)}, accept=${approved}, stepIndices=[${effectiveStepIndex}])`);
|
||||
const ackResult = await ctx.sdk.ls.rawRPC('acknowledgeCodeActionStep', {
|
||||
cascadeId: sessionId,
|
||||
accept: approved,
|
||||
|
||||
Reference in New Issue
Block a user