@quantiya/codevibe-antigravity-plugin 2.0.17 → 2.0.19
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.js +452 -62
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -46,7 +46,7 @@ var fs5 = __toESM(require("fs"));
|
|
|
46
46
|
var os5 = __toESM(require("os"));
|
|
47
47
|
var import_child_process3 = require("child_process");
|
|
48
48
|
var import_util3 = require("util");
|
|
49
|
-
var
|
|
49
|
+
var import_codevibe_core5 = require("@quantiya/codevibe-core");
|
|
50
50
|
|
|
51
51
|
// src/logger.ts
|
|
52
52
|
var import_os = __toESM(require("os"));
|
|
@@ -1961,7 +1961,7 @@ var ApprovalDetector = class extends import_events3.EventEmitter {
|
|
|
1961
1961
|
let matchedKey = null;
|
|
1962
1962
|
for (const [key, call] of this.pendingCalls.entries()) {
|
|
1963
1963
|
if (call.conversationId !== args.conversationId) continue;
|
|
1964
|
-
if (call.toolType !== args.toolType) continue;
|
|
1964
|
+
if (args.toolType !== "GENERIC" && call.toolType !== args.toolType) continue;
|
|
1965
1965
|
const expected = call.intentStepIndex + call.toolCallIndex + 1;
|
|
1966
1966
|
if (expected !== args.resultStepIndex) continue;
|
|
1967
1967
|
if (typeof args.resultByteOffset === "number") {
|
|
@@ -3010,6 +3010,255 @@ function truncate(s, maxBytes) {
|
|
|
3010
3010
|
return s.slice(0, lo) + ` [...truncated ${s.length - lo} chars]`;
|
|
3011
3011
|
}
|
|
3012
3012
|
|
|
3013
|
+
// src/tool-activity-integration.ts
|
|
3014
|
+
var import_codevibe_core4 = require("@quantiya/codevibe-core");
|
|
3015
|
+
var TOOL_ACTIVITY_CONTENT = "tool_activity";
|
|
3016
|
+
var DEFAULT_WINDOW_MS = 1e4;
|
|
3017
|
+
var DEFAULT_MAX_OCCURRENCES = 50;
|
|
3018
|
+
var DEFAULT_SHUTDOWN_DRAIN_MS = 3e3;
|
|
3019
|
+
function terminalSessionError(sessionId) {
|
|
3020
|
+
return Object.assign(new Error(`Antigravity session is no longer active: ${sessionId}`), {
|
|
3021
|
+
errorType: "SessionTerminal"
|
|
3022
|
+
});
|
|
3023
|
+
}
|
|
3024
|
+
function friendlyHookToolName(rawName, resultType) {
|
|
3025
|
+
switch (rawName?.toUpperCase()) {
|
|
3026
|
+
case "RUN_COMMAND":
|
|
3027
|
+
return "Bash";
|
|
3028
|
+
case "LIST_DIR":
|
|
3029
|
+
return "LS";
|
|
3030
|
+
case "GREP_SEARCH":
|
|
3031
|
+
return "Grep";
|
|
3032
|
+
case "VIEW_FILE":
|
|
3033
|
+
return "Read";
|
|
3034
|
+
case "WRITE_TO_FILE":
|
|
3035
|
+
case "REPLACE_FILE_CONTENT":
|
|
3036
|
+
case "MULTI_REPLACE_FILE_CONTENT":
|
|
3037
|
+
return "Edit";
|
|
3038
|
+
case "SEARCH_WEB":
|
|
3039
|
+
return "WebSearch";
|
|
3040
|
+
case "READ_URL_CONTENT":
|
|
3041
|
+
return "WebFetch";
|
|
3042
|
+
case "GENERATE_IMAGE":
|
|
3043
|
+
return "ImageGen";
|
|
3044
|
+
case "INVOKE_SUBAGENT":
|
|
3045
|
+
case "DEFINE_SUBAGENT":
|
|
3046
|
+
case "MANAGE_SUBAGENTS":
|
|
3047
|
+
return "Agent";
|
|
3048
|
+
case "SEND_MESSAGE":
|
|
3049
|
+
return "SendMessage";
|
|
3050
|
+
case "LIST_PERMISSIONS":
|
|
3051
|
+
return "Permissions";
|
|
3052
|
+
case "MANAGE_TASK":
|
|
3053
|
+
return "Task";
|
|
3054
|
+
case "SCHEDULE":
|
|
3055
|
+
return "Schedule";
|
|
3056
|
+
case "ASK_PERMISSION":
|
|
3057
|
+
case "ASK_QUESTION":
|
|
3058
|
+
return "AskUserQuestion";
|
|
3059
|
+
default:
|
|
3060
|
+
return transcriptTypeToFriendlyToolName(resultType);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
function commandTarget(command) {
|
|
3064
|
+
return command?.trim().split(/\s+/)[0] || void 0;
|
|
3065
|
+
}
|
|
3066
|
+
function agyOccurrenceId(emit, matchedCall) {
|
|
3067
|
+
const identity = matchedCall ? {
|
|
3068
|
+
conversationId: matchedCall.conversationId,
|
|
3069
|
+
intentCreatedAt: matchedCall.intentCreatedAt,
|
|
3070
|
+
intentStepIndex: matchedCall.intentStepIndex,
|
|
3071
|
+
toolCallIndex: matchedCall.toolCallIndex,
|
|
3072
|
+
rawToolName: matchedCall.rawToolName ?? null,
|
|
3073
|
+
command: matchedCall.command ?? null,
|
|
3074
|
+
filePath: matchedCall.filePath ?? null,
|
|
3075
|
+
cwd: matchedCall.cwd ?? null
|
|
3076
|
+
} : {
|
|
3077
|
+
conversationId: emit.conversationId,
|
|
3078
|
+
createdAt: emit.event.created_at,
|
|
3079
|
+
stepIndex: emit.event.step_index,
|
|
3080
|
+
type: emit.event.type,
|
|
3081
|
+
contentDigest: (0, import_codevibe_core4.digestOf)(emit.event.content ?? "")
|
|
3082
|
+
};
|
|
3083
|
+
return `agy:${(0, import_codevibe_core4.digestOf)(identity).slice("sha256:".length)}`;
|
|
3084
|
+
}
|
|
3085
|
+
var AgyToolActivityIntegration = class {
|
|
3086
|
+
constructor(deps) {
|
|
3087
|
+
this.consolidator = null;
|
|
3088
|
+
this.backendSessionId = null;
|
|
3089
|
+
this.sessionsToRehome = /* @__PURE__ */ new Set();
|
|
3090
|
+
this.deps = deps;
|
|
3091
|
+
}
|
|
3092
|
+
isActive() {
|
|
3093
|
+
return this.consolidator !== null;
|
|
3094
|
+
}
|
|
3095
|
+
async start(sessionId) {
|
|
3096
|
+
if (this.consolidator && this.backendSessionId === sessionId) return;
|
|
3097
|
+
if (this.consolidator) await this.stop();
|
|
3098
|
+
const outbox = new import_codevibe_core4.ToolActivityOutbox({
|
|
3099
|
+
agent: "agy",
|
|
3100
|
+
logger: this.deps.logger,
|
|
3101
|
+
root: this.deps.root
|
|
3102
|
+
});
|
|
3103
|
+
await this.rehomePendingWindows(outbox, sessionId);
|
|
3104
|
+
const ledger = new import_codevibe_core4.ToolActivityLedger({
|
|
3105
|
+
agent: "agy",
|
|
3106
|
+
sessionId,
|
|
3107
|
+
logger: this.deps.logger,
|
|
3108
|
+
root: this.deps.root
|
|
3109
|
+
});
|
|
3110
|
+
const consolidator = new import_codevibe_core4.ToolActivityConsolidator({
|
|
3111
|
+
agent: "agy",
|
|
3112
|
+
sessionId,
|
|
3113
|
+
logger: this.deps.logger,
|
|
3114
|
+
outbox,
|
|
3115
|
+
ledger,
|
|
3116
|
+
transport: this.buildTransport(),
|
|
3117
|
+
windowMs: this.deps.windowMs ?? DEFAULT_WINDOW_MS,
|
|
3118
|
+
maxOccurrences: DEFAULT_MAX_OCCURRENCES
|
|
3119
|
+
});
|
|
3120
|
+
this.backendSessionId = sessionId;
|
|
3121
|
+
try {
|
|
3122
|
+
await consolidator.init();
|
|
3123
|
+
this.consolidator = consolidator;
|
|
3124
|
+
this.deps.logger.info("[tool-activity] Agy consolidator started", { sessionId });
|
|
3125
|
+
} catch (error) {
|
|
3126
|
+
this.backendSessionId = null;
|
|
3127
|
+
await consolidator.shutdown().catch(() => void 0);
|
|
3128
|
+
throw error;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
async observeResult(emit, matchedCall) {
|
|
3132
|
+
const consolidator = this.consolidator;
|
|
3133
|
+
if (!consolidator) return void 0;
|
|
3134
|
+
const resultType = emit.event.type;
|
|
3135
|
+
const target = matchedCall?.filePath ?? commandTarget(matchedCall?.command) ?? matchedCall?.cwd;
|
|
3136
|
+
const inputIdentity = matchedCall ? {
|
|
3137
|
+
rawToolName: matchedCall.rawToolName ?? null,
|
|
3138
|
+
command: matchedCall.command ?? null,
|
|
3139
|
+
filePath: matchedCall.filePath ?? null,
|
|
3140
|
+
cwd: matchedCall.cwd ?? null
|
|
3141
|
+
} : void 0;
|
|
3142
|
+
return consolidator.observe({
|
|
3143
|
+
id: agyOccurrenceId(emit, matchedCall),
|
|
3144
|
+
tool: friendlyHookToolName(matchedCall?.rawToolName, resultType),
|
|
3145
|
+
...target ? { normalizedTarget: target } : {},
|
|
3146
|
+
ts: emit.event.created_at,
|
|
3147
|
+
byteOffset: emit.byteOffset,
|
|
3148
|
+
...inputIdentity ? { digest: (0, import_codevibe_core4.digestOf)(inputIdentity) } : {}
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
async flush() {
|
|
3152
|
+
await this.consolidator?.flush();
|
|
3153
|
+
}
|
|
3154
|
+
async stop() {
|
|
3155
|
+
const consolidator = this.consolidator;
|
|
3156
|
+
this.consolidator = null;
|
|
3157
|
+
if (!consolidator) {
|
|
3158
|
+
this.backendSessionId = null;
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
await consolidator.drainSends(this.deps.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS).catch(() => void 0);
|
|
3162
|
+
await consolidator.shutdown();
|
|
3163
|
+
this.backendSessionId = null;
|
|
3164
|
+
}
|
|
3165
|
+
/** Preserve a backend-retired session's durable windows for the replacement. */
|
|
3166
|
+
async prepareForReplacement() {
|
|
3167
|
+
const consolidator = this.consolidator;
|
|
3168
|
+
const sessionId = this.backendSessionId;
|
|
3169
|
+
this.consolidator = null;
|
|
3170
|
+
this.backendSessionId = null;
|
|
3171
|
+
if (!consolidator || !sessionId) return;
|
|
3172
|
+
this.sessionsToRehome.add(sessionId);
|
|
3173
|
+
await consolidator.shutdown();
|
|
3174
|
+
await consolidator.drainSends(this.deps.shutdownDrainMs ?? DEFAULT_SHUTDOWN_DRAIN_MS).catch(() => void 0);
|
|
3175
|
+
}
|
|
3176
|
+
async rehomePendingWindows(outbox, replacementSessionId) {
|
|
3177
|
+
const sessionIds = await outbox.enumerateSessions();
|
|
3178
|
+
for (const oldSessionId of sessionIds) {
|
|
3179
|
+
if (oldSessionId === replacementSessionId) continue;
|
|
3180
|
+
const pending = await outbox.listPending(oldSessionId);
|
|
3181
|
+
if (pending.valid.length === 0) continue;
|
|
3182
|
+
let eligible = this.sessionsToRehome.has(oldSessionId);
|
|
3183
|
+
if (!eligible && this.deps.getBackendSession) {
|
|
3184
|
+
try {
|
|
3185
|
+
const oldSession = await this.deps.getBackendSession(oldSessionId);
|
|
3186
|
+
const replacement = this.deps.getSession();
|
|
3187
|
+
eligible = Boolean(
|
|
3188
|
+
oldSession && replacement && oldSession.status === "INACTIVE" && oldSession.userId === replacement.userId && oldSession.projectPath === replacement.projectPath
|
|
3189
|
+
);
|
|
3190
|
+
} catch (error) {
|
|
3191
|
+
this.deps.logger.warn("[tool-activity] Could not verify prior Agy session for rehome", {
|
|
3192
|
+
oldSessionId,
|
|
3193
|
+
error: String(error)
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
if (!eligible) continue;
|
|
3198
|
+
for (const envelope of pending.valid) {
|
|
3199
|
+
const rehomed = { ...envelope, backendSessionId: replacementSessionId };
|
|
3200
|
+
await outbox.persist(replacementSessionId, rehomed);
|
|
3201
|
+
await outbox.prune(oldSessionId, envelope.clientEventId);
|
|
3202
|
+
}
|
|
3203
|
+
this.sessionsToRehome.delete(oldSessionId);
|
|
3204
|
+
this.deps.logger.info("[tool-activity] Rehomed durable Agy windows", {
|
|
3205
|
+
oldSessionId,
|
|
3206
|
+
replacementSessionId,
|
|
3207
|
+
count: pending.valid.length
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
buildTransport() {
|
|
3212
|
+
const sendOnce = async (sessionId, envelope, override) => {
|
|
3213
|
+
const session = this.deps.getSession();
|
|
3214
|
+
if (!session || session.sessionId !== sessionId) {
|
|
3215
|
+
throw terminalSessionError(sessionId);
|
|
3216
|
+
}
|
|
3217
|
+
const encrypted = (0, import_codevibe_core4.encryptForEmit)(
|
|
3218
|
+
override?.sessionKey ?? session.sessionKey ?? null,
|
|
3219
|
+
session.sessionIsEncrypted ?? false,
|
|
3220
|
+
TOOL_ACTIVITY_CONTENT,
|
|
3221
|
+
{ manifest: envelope.manifest }
|
|
3222
|
+
);
|
|
3223
|
+
if (!encrypted) {
|
|
3224
|
+
throw new Error("[tool-activity] encrypted Agy session has no key (retryable)");
|
|
3225
|
+
}
|
|
3226
|
+
const sessionKeyGen = override?.sessionKeyGen ?? session.sessionKeyGen;
|
|
3227
|
+
const input = {
|
|
3228
|
+
sessionId,
|
|
3229
|
+
type: import_codevibe_core4.EventType.TOOL_ACTIVITY,
|
|
3230
|
+
source: import_codevibe_core4.EventSource.DESKTOP,
|
|
3231
|
+
content: encrypted.content,
|
|
3232
|
+
metadata: encrypted.metadata,
|
|
3233
|
+
timestamp: envelope.windowStart,
|
|
3234
|
+
clientEventId: envelope.clientEventId,
|
|
3235
|
+
...encrypted.isEncrypted ? { isEncrypted: true } : {},
|
|
3236
|
+
...encrypted.isEncrypted && sessionKeyGen ? { expectedSessionKeyGen: sessionKeyGen } : {}
|
|
3237
|
+
};
|
|
3238
|
+
await this.deps.createEvent(input);
|
|
3239
|
+
};
|
|
3240
|
+
return {
|
|
3241
|
+
send: async (sessionId, envelope) => {
|
|
3242
|
+
try {
|
|
3243
|
+
await sendOnce(sessionId, envelope);
|
|
3244
|
+
} catch (error) {
|
|
3245
|
+
if ((0, import_codevibe_core4.isTerminalSessionError)(error)) {
|
|
3246
|
+
throw new Error(`[tool-activity] Agy session ${sessionId} retired; awaiting replacement`);
|
|
3247
|
+
}
|
|
3248
|
+
if ((0, import_codevibe_core4.isSessionKeyStaleError)(error) && this.deps.refreshSessionKey) {
|
|
3249
|
+
const refreshed = await this.deps.refreshSessionKey(sessionId);
|
|
3250
|
+
if (refreshed) {
|
|
3251
|
+
await sendOnce(sessionId, envelope, refreshed);
|
|
3252
|
+
return;
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
throw error;
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
};
|
|
3259
|
+
}
|
|
3260
|
+
};
|
|
3261
|
+
|
|
3013
3262
|
// src/server.ts
|
|
3014
3263
|
var MOBILE_PROMPT_FLOOR_RECENCY_MS = 12e4;
|
|
3015
3264
|
var LAUNCH_SETTLE_TIMEOUT_MS = 3e3;
|
|
@@ -3051,6 +3300,8 @@ var McpServer = class _McpServer {
|
|
|
3051
3300
|
* under this single sessionId. /resume does NOT create a new
|
|
3052
3301
|
* session — same row. */
|
|
3053
3302
|
this.session = null;
|
|
3303
|
+
this.transcriptHandlers = /* @__PURE__ */ new Set();
|
|
3304
|
+
this.toolActivityKeyRefresh = null;
|
|
3054
3305
|
// E1 (§4/§6a-4) — per-promptId durable raise entries for the agy producer.
|
|
3055
3306
|
// Keyed by the E1 promptId (= derive(ownerToken)); each holds the SECRET
|
|
3056
3307
|
// ownerToken so a resume/retirement re-raise re-commits the SAME promptId
|
|
@@ -3191,7 +3442,21 @@ var McpServer = class _McpServer {
|
|
|
3191
3442
|
this.wrapperPid = options.wrapperPid ?? null;
|
|
3192
3443
|
this.cliLogPath = options.cliLogPath ?? null;
|
|
3193
3444
|
this.launchSettleTimeoutMs = options.launchSettleTimeoutMs ?? LAUNCH_SETTLE_TIMEOUT_MS;
|
|
3194
|
-
this.appSyncClient = options.appSyncClient ?? new
|
|
3445
|
+
this.appSyncClient = options.appSyncClient ?? new import_codevibe_core5.AppSyncClient();
|
|
3446
|
+
this.toolActivity = options.toolActivityIntegration === void 0 ? new AgyToolActivityIntegration({
|
|
3447
|
+
logger,
|
|
3448
|
+
createEvent: (input) => this.appSyncClient.createEvent(input),
|
|
3449
|
+
getSession: () => this.session,
|
|
3450
|
+
refreshSessionKey: (sessionId) => this.refreshToolActivitySessionKey(sessionId),
|
|
3451
|
+
getBackendSession: async (sessionId) => {
|
|
3452
|
+
const session = await this.appSyncClient.getSession(sessionId);
|
|
3453
|
+
return session ? {
|
|
3454
|
+
status: session.status,
|
|
3455
|
+
userId: session.userId,
|
|
3456
|
+
projectPath: session.projectPath
|
|
3457
|
+
} : null;
|
|
3458
|
+
}
|
|
3459
|
+
}) : options.toolActivityIntegration;
|
|
3195
3460
|
this.workingDirectory = process.cwd();
|
|
3196
3461
|
this.approvalDetector = options.approvalDetector ?? new ApprovalDetector(options.detectorOptions);
|
|
3197
3462
|
this.paneObserver = options.paneObserver ?? new TmuxPaneObserver();
|
|
@@ -3243,9 +3508,9 @@ var McpServer = class _McpServer {
|
|
|
3243
3508
|
this.stopRequestedDuringCreate = false;
|
|
3244
3509
|
this.registerSignalHandlers();
|
|
3245
3510
|
this.startTmuxLifecycleMonitor();
|
|
3246
|
-
await (0,
|
|
3511
|
+
await (0, import_codevibe_core5.registerDeviceEncryptionKey)(this.appSyncClient, logger);
|
|
3247
3512
|
if (!this.started) return { httpPort: 0 };
|
|
3248
|
-
(0,
|
|
3513
|
+
(0, import_codevibe_core5.startDeviceKeyWatcher)(this.appSyncClient, logger);
|
|
3249
3514
|
try {
|
|
3250
3515
|
const swept = await this.appSyncClient.sweepOrphanSessions({
|
|
3251
3516
|
agentType: "ANTIGRAVITY",
|
|
@@ -3280,9 +3545,14 @@ var McpServer = class _McpServer {
|
|
|
3280
3545
|
logger.error("transcript tailer error (non-fatal)", { error: String(err) });
|
|
3281
3546
|
});
|
|
3282
3547
|
this.addListener(this.transcriptTailer, "event", (emit) => {
|
|
3283
|
-
|
|
3548
|
+
const task = this.handleTranscriptEmit(emit).catch((err) => {
|
|
3284
3549
|
logger.error("handleTranscriptEmit failed", { error: String(err) });
|
|
3285
3550
|
});
|
|
3551
|
+
this.transcriptHandlers.add(task);
|
|
3552
|
+
void task.then(
|
|
3553
|
+
() => this.transcriptHandlers.delete(task),
|
|
3554
|
+
() => this.transcriptHandlers.delete(task)
|
|
3555
|
+
);
|
|
3286
3556
|
});
|
|
3287
3557
|
this.addListener(this.transcriptTailer, "conversation-discovered", (conversationId) => {
|
|
3288
3558
|
void this.handleConversationDiscovered(conversationId).catch((err) => {
|
|
@@ -3378,17 +3648,29 @@ var McpServer = class _McpServer {
|
|
|
3378
3648
|
}
|
|
3379
3649
|
}
|
|
3380
3650
|
this.listenerHandles = [];
|
|
3651
|
+
await Promise.allSettled([...this.transcriptHandlers]);
|
|
3381
3652
|
if (this.session) {
|
|
3382
3653
|
try {
|
|
3383
3654
|
this.appSyncClient.stopHeartbeat(this.session.sessionId);
|
|
3384
3655
|
} catch {
|
|
3385
3656
|
}
|
|
3657
|
+
try {
|
|
3658
|
+
await this.toolActivity?.stop();
|
|
3659
|
+
} catch (err) {
|
|
3660
|
+
logger.warn("[tool-activity] Agy shutdown drain failed", { error: String(err) });
|
|
3661
|
+
}
|
|
3386
3662
|
const retired = await this.deactivateLaunchRow(this.session.sessionId);
|
|
3387
3663
|
if (!retired) {
|
|
3388
3664
|
hostedRetirementError = new Error(
|
|
3389
3665
|
`Failed to retire hosted Antigravity session ${this.session.sessionId}`
|
|
3390
3666
|
);
|
|
3391
3667
|
}
|
|
3668
|
+
} else {
|
|
3669
|
+
try {
|
|
3670
|
+
await this.toolActivity?.stop();
|
|
3671
|
+
} catch (err) {
|
|
3672
|
+
logger.warn("[tool-activity] Agy shutdown cleanup failed", { error: String(err) });
|
|
3673
|
+
}
|
|
3392
3674
|
}
|
|
3393
3675
|
this.unregisterSignalHandlers();
|
|
3394
3676
|
try {
|
|
@@ -3525,8 +3807,9 @@ var McpServer = class _McpServer {
|
|
|
3525
3807
|
const projectPath = process.cwd();
|
|
3526
3808
|
this.pendingLaunchSessionId = sessionId;
|
|
3527
3809
|
let sessionKey = null;
|
|
3810
|
+
let sessionKeyGen = null;
|
|
3528
3811
|
try {
|
|
3529
|
-
const result = await (0,
|
|
3812
|
+
const result = await (0, import_codevibe_core5.resumeOrCreateSession)(
|
|
3530
3813
|
{
|
|
3531
3814
|
sessionId,
|
|
3532
3815
|
userId,
|
|
@@ -3542,6 +3825,7 @@ var McpServer = class _McpServer {
|
|
|
3542
3825
|
logger
|
|
3543
3826
|
);
|
|
3544
3827
|
sessionKey = result.sessionKey ?? null;
|
|
3828
|
+
sessionKeyGen = result.sessionKeyGen ?? null;
|
|
3545
3829
|
} catch (err) {
|
|
3546
3830
|
if (this.pendingLaunchSessionId === sessionId) this.pendingLaunchSessionId = null;
|
|
3547
3831
|
const msg = String(err);
|
|
@@ -3583,6 +3867,7 @@ var McpServer = class _McpServer {
|
|
|
3583
3867
|
subscriptionActive: false,
|
|
3584
3868
|
metadata: { wrapperPid: this.wrapperPid ?? void 0, launch: true },
|
|
3585
3869
|
sessionKey,
|
|
3870
|
+
sessionKeyGen,
|
|
3586
3871
|
// (#638) A null key here means a genuinely unencrypted/legacy session
|
|
3587
3872
|
// (resumeOrCreateSession THROWS ENCRYPTED_SESSION_NO_KEY for an encrypted
|
|
3588
3873
|
// session this device can't key, handled in the catch above — no session
|
|
@@ -3592,6 +3877,14 @@ var McpServer = class _McpServer {
|
|
|
3592
3877
|
sessionIsEncrypted: !!sessionKey
|
|
3593
3878
|
};
|
|
3594
3879
|
this.pendingLaunchSessionId = null;
|
|
3880
|
+
try {
|
|
3881
|
+
await this.toolActivity?.start(sessionId);
|
|
3882
|
+
} catch (error) {
|
|
3883
|
+
logger.warn("[tool-activity] Agy consolidator unavailable; retaining raw TOOL_USE fallback", {
|
|
3884
|
+
sessionId,
|
|
3885
|
+
error: String(error)
|
|
3886
|
+
});
|
|
3887
|
+
}
|
|
3595
3888
|
try {
|
|
3596
3889
|
const stop = this.appSyncClient.subscribeToEvents(
|
|
3597
3890
|
sessionId,
|
|
@@ -3709,20 +4002,36 @@ var McpServer = class _McpServer {
|
|
|
3709
4002
|
this.approvalDetector.registerPendingCall(reg);
|
|
3710
4003
|
}
|
|
3711
4004
|
for (const input of mapped.events) {
|
|
3712
|
-
if (input.type ===
|
|
4005
|
+
if (input.type === import_codevibe_core5.EventType.USER_PROMPT || input.type === import_codevibe_core5.EventType.ASSISTANT_RESPONSE || input.type === import_codevibe_core5.EventType.NOTIFICATION) {
|
|
4006
|
+
await this.flushToolActivityAtBoundary(input.type);
|
|
4007
|
+
}
|
|
4008
|
+
if (input.type === import_codevibe_core5.EventType.TOOL_USE && typeof input.metadata === "object" && input.metadata) {
|
|
3713
4009
|
const meta = input.metadata;
|
|
3714
4010
|
const resultStep = typeof meta.step_index === "number" ? meta.step_index : void 0;
|
|
3715
4011
|
const agyType = typeof meta.agy_type === "string" ? meta.agy_type : void 0;
|
|
3716
4012
|
if (resultStep !== void 0 && agyType) {
|
|
3717
|
-
this.approvalDetector.clearOnResultEvent({
|
|
4013
|
+
const matchedCall = this.approvalDetector.clearOnResultEvent({
|
|
3718
4014
|
conversationId,
|
|
3719
4015
|
resultStepIndex: resultStep,
|
|
3720
4016
|
toolType: agyType,
|
|
3721
4017
|
resultByteOffset: byteOffset
|
|
3722
4018
|
});
|
|
4019
|
+
try {
|
|
4020
|
+
const disposition = await this.toolActivity?.observeResult(emit, matchedCall);
|
|
4021
|
+
if (disposition && disposition !== "closed") {
|
|
4022
|
+
continue;
|
|
4023
|
+
}
|
|
4024
|
+
} catch (error) {
|
|
4025
|
+
logger.warn("[tool-activity] Agy result observation failed; emitting raw TOOL_USE fallback", {
|
|
4026
|
+
sessionId: session.sessionId,
|
|
4027
|
+
conversationId,
|
|
4028
|
+
resultStep,
|
|
4029
|
+
error: String(error)
|
|
4030
|
+
});
|
|
4031
|
+
}
|
|
3723
4032
|
}
|
|
3724
4033
|
}
|
|
3725
|
-
if (input.type ===
|
|
4034
|
+
if (input.type === import_codevibe_core5.EventType.USER_PROMPT && input.source === import_codevibe_core5.EventSource.DESKTOP) {
|
|
3726
4035
|
const clearedPromptIds = this.approvalDetector.onPanePromptCleared();
|
|
3727
4036
|
await this.resolveAcceptedInputPrompts(session, clearedPromptIds);
|
|
3728
4037
|
const dedupe = this.mobileDeduper.consumeIfDuplicate(session.sessionId, input.content);
|
|
@@ -3735,9 +4044,7 @@ var McpServer = class _McpServer {
|
|
|
3735
4044
|
}
|
|
3736
4045
|
}
|
|
3737
4046
|
try {
|
|
3738
|
-
|
|
3739
|
-
if (!outbound) continue;
|
|
3740
|
-
await this.appSyncClient.createEvent(outbound);
|
|
4047
|
+
if (!await this.createEncryptedEvent(session, input)) continue;
|
|
3741
4048
|
} catch (err) {
|
|
3742
4049
|
logger.error("createEvent failed", {
|
|
3743
4050
|
sessionId: session.sessionId,
|
|
@@ -3747,6 +4054,67 @@ var McpServer = class _McpServer {
|
|
|
3747
4054
|
}
|
|
3748
4055
|
}
|
|
3749
4056
|
}
|
|
4057
|
+
async flushToolActivityAtBoundary(boundary) {
|
|
4058
|
+
try {
|
|
4059
|
+
await this.toolActivity?.flush();
|
|
4060
|
+
} catch (error) {
|
|
4061
|
+
logger.warn("[tool-activity] Agy boundary flush failed; retaining individual event", {
|
|
4062
|
+
boundary,
|
|
4063
|
+
error: String(error)
|
|
4064
|
+
});
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
async refreshToolActivitySessionKey(sessionId) {
|
|
4068
|
+
if (this.toolActivityKeyRefresh?.sessionId === sessionId) {
|
|
4069
|
+
return this.toolActivityKeyRefresh.promise;
|
|
4070
|
+
}
|
|
4071
|
+
const promise = this.loadAndInstallToolActivitySessionKey(sessionId);
|
|
4072
|
+
this.toolActivityKeyRefresh = { sessionId, promise };
|
|
4073
|
+
try {
|
|
4074
|
+
return await promise;
|
|
4075
|
+
} finally {
|
|
4076
|
+
if (this.toolActivityKeyRefresh?.promise === promise) {
|
|
4077
|
+
this.toolActivityKeyRefresh = null;
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
async loadAndInstallToolActivitySessionKey(sessionId) {
|
|
4082
|
+
const lifecycleGen = this.lifecycleGen;
|
|
4083
|
+
const expectedSession = this.session;
|
|
4084
|
+
try {
|
|
4085
|
+
const session = await this.appSyncClient.getSession(sessionId);
|
|
4086
|
+
if (session?.status === import_codevibe_core5.SessionStatus.INACTIVE) {
|
|
4087
|
+
const terminal = new Error(`Antigravity session is no longer active: ${sessionId}`);
|
|
4088
|
+
terminal.errorType = "SessionTerminal";
|
|
4089
|
+
throw terminal;
|
|
4090
|
+
}
|
|
4091
|
+
if (!session || session.status !== import_codevibe_core5.SessionStatus.ACTIVE || !session.isEncrypted) {
|
|
4092
|
+
return null;
|
|
4093
|
+
}
|
|
4094
|
+
const sessionKeyGen = session.sessionKeyGen ?? null;
|
|
4095
|
+
const encryptedKeys = session.encryptedKeys ?? [];
|
|
4096
|
+
if (encryptedKeys.length === 0) return null;
|
|
4097
|
+
const sessionKey = await import_codevibe_core5.keychainManager.getSessionKey(
|
|
4098
|
+
sessionId,
|
|
4099
|
+
encryptedKeys,
|
|
4100
|
+
sessionKeyGen
|
|
4101
|
+
);
|
|
4102
|
+
if (!sessionKey) return null;
|
|
4103
|
+
const liveSession = this.session;
|
|
4104
|
+
if (lifecycleGen !== this.lifecycleGen || !this.started || !expectedSession || liveSession !== expectedSession || liveSession.sessionId !== sessionId) return null;
|
|
4105
|
+
liveSession.sessionKey = sessionKey;
|
|
4106
|
+
liveSession.sessionKeyGen = sessionKeyGen;
|
|
4107
|
+
liveSession.sessionIsEncrypted = true;
|
|
4108
|
+
return { sessionKey, sessionKeyGen };
|
|
4109
|
+
} catch (error) {
|
|
4110
|
+
if ((0, import_codevibe_core5.isTerminalSessionError)(error)) throw error;
|
|
4111
|
+
logger.warn("[tool-activity] Agy session-key refresh failed; window remains retryable", {
|
|
4112
|
+
sessionId,
|
|
4113
|
+
error: String(error)
|
|
4114
|
+
});
|
|
4115
|
+
return null;
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
3750
4118
|
/**
|
|
3751
4119
|
* (#638) THE single encrypt-or-fail-closed decision for EVERY mobile-visible
|
|
3752
4120
|
* emit, delegated to @quantiya/codevibe-core's encryptForEmit so all three
|
|
@@ -3759,7 +4127,7 @@ var McpServer = class _McpServer {
|
|
|
3759
4127
|
* an encrypted keyless one (drop).
|
|
3760
4128
|
*/
|
|
3761
4129
|
encryptOutbound(session, input) {
|
|
3762
|
-
const enc = (0,
|
|
4130
|
+
const enc = (0, import_codevibe_core5.encryptForEmit)(
|
|
3763
4131
|
session.sessionKey ?? null,
|
|
3764
4132
|
session.sessionIsEncrypted ?? false,
|
|
3765
4133
|
input.content,
|
|
@@ -3779,9 +4147,28 @@ var McpServer = class _McpServer {
|
|
|
3779
4147
|
...input,
|
|
3780
4148
|
content: enc.content,
|
|
3781
4149
|
metadata: enc.metadata,
|
|
3782
|
-
isEncrypted: true
|
|
4150
|
+
isEncrypted: true,
|
|
4151
|
+
...session.sessionKeyGen ? { expectedSessionKeyGen: session.sessionKeyGen } : {}
|
|
3783
4152
|
};
|
|
3784
4153
|
}
|
|
4154
|
+
/** Encrypt and send one individual event, refreshing a stale session key once. */
|
|
4155
|
+
async createEncryptedEvent(session, input) {
|
|
4156
|
+
const lifecycleGen = this.lifecycleGen;
|
|
4157
|
+
const sendOnce = async () => {
|
|
4158
|
+
if (lifecycleGen !== this.lifecycleGen || !this.started || this.session !== session || input.sessionId !== session.sessionId) return null;
|
|
4159
|
+
const outbound = this.encryptOutbound(session, input);
|
|
4160
|
+
if (!outbound) return null;
|
|
4161
|
+
return this.appSyncClient.createEvent(outbound);
|
|
4162
|
+
};
|
|
4163
|
+
try {
|
|
4164
|
+
return await sendOnce();
|
|
4165
|
+
} catch (error) {
|
|
4166
|
+
if (!(0, import_codevibe_core5.isSessionKeyStaleError)(error)) throw error;
|
|
4167
|
+
const refreshed = await this.refreshToolActivitySessionKey(session.sessionId);
|
|
4168
|
+
if (!refreshed) throw error;
|
|
4169
|
+
return sendOnce();
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
3785
4172
|
// ─── E1 missed-prompt recovery (§4/§6a) — in-memory ledger + raise helpers ───
|
|
3786
4173
|
//
|
|
3787
4174
|
// The authoritative ledger is the in-memory `e1PromptRaises` Map (see its field
|
|
@@ -3797,7 +4184,7 @@ var McpServer = class _McpServer {
|
|
|
3797
4184
|
* NOTIFICATION carrier, no fabricated options).
|
|
3798
4185
|
*/
|
|
3799
4186
|
newE1Raise(sessionId, mobileActionable, title) {
|
|
3800
|
-
const { record } = (0,
|
|
4187
|
+
const { record } = (0, import_codevibe_core5.newPromptRaise)({
|
|
3801
4188
|
sessionId,
|
|
3802
4189
|
producerKind: "PLUGIN_APPROVAL",
|
|
3803
4190
|
mobileActionable,
|
|
@@ -3833,8 +4220,8 @@ var McpServer = class _McpServer {
|
|
|
3833
4220
|
try {
|
|
3834
4221
|
const ack = await this.appSyncClient.createEvent({
|
|
3835
4222
|
sessionId: entry.record.sessionId || sessionId,
|
|
3836
|
-
type:
|
|
3837
|
-
source:
|
|
4223
|
+
type: import_codevibe_core5.EventType.NOTIFICATION,
|
|
4224
|
+
source: import_codevibe_core5.EventSource.DESKTOP,
|
|
3838
4225
|
resolvedPromptId: promptId,
|
|
3839
4226
|
ownerToken: entry.record.ownerToken
|
|
3840
4227
|
});
|
|
@@ -3895,17 +4282,16 @@ var McpServer = class _McpServer {
|
|
|
3895
4282
|
const bannerText = "\u26A0\uFE0F A prompt is waiting in your desktop terminal, but its options could not be shown here. Please answer it on your desktop.";
|
|
3896
4283
|
const input = {
|
|
3897
4284
|
sessionId: session.sessionId,
|
|
3898
|
-
type:
|
|
3899
|
-
source:
|
|
4285
|
+
type: import_codevibe_core5.EventType.NOTIFICATION,
|
|
4286
|
+
source: import_codevibe_core5.EventSource.DESKTOP,
|
|
3900
4287
|
content: bannerText,
|
|
3901
4288
|
metadata: { e1SuppressedPrompt: true },
|
|
3902
|
-
...(0,
|
|
4289
|
+
...(0, import_codevibe_core5.raiseFieldsFromRecord)(record),
|
|
3903
4290
|
notificationText: bannerText,
|
|
3904
|
-
timestamp: (0,
|
|
4291
|
+
timestamp: (0, import_codevibe_core5.prepareEventTimestamp)({ orderingKey: session.sessionId })
|
|
3905
4292
|
};
|
|
3906
|
-
|
|
3907
|
-
if (!
|
|
3908
|
-
await this.appSyncClient.createEvent(outbound);
|
|
4293
|
+
await this.flushToolActivityAtBoundary(import_codevibe_core5.EventType.NOTIFICATION);
|
|
4294
|
+
if (!await this.createEncryptedEvent(session, input)) return false;
|
|
3909
4295
|
logger.info("E1: emitted suppressed-prompt badge carrier (mobileActionable=false)", {
|
|
3910
4296
|
sessionId: session.sessionId,
|
|
3911
4297
|
promptId: record.promptId
|
|
@@ -4072,16 +4458,15 @@ var McpServer = class _McpServer {
|
|
|
4072
4458
|
if (actionable) {
|
|
4073
4459
|
const input = {
|
|
4074
4460
|
sessionId: session.sessionId,
|
|
4075
|
-
type:
|
|
4076
|
-
source:
|
|
4461
|
+
type: import_codevibe_core5.EventType.INTERACTIVE_PROMPT,
|
|
4462
|
+
source: import_codevibe_core5.EventSource.DESKTOP,
|
|
4077
4463
|
content: entry.contentPlain,
|
|
4078
4464
|
metadata: entry.metadataPlain ?? {},
|
|
4079
|
-
...(0,
|
|
4080
|
-
timestamp: (0,
|
|
4465
|
+
...(0, import_codevibe_core5.raiseFieldsFromRecord)(rec),
|
|
4466
|
+
timestamp: (0, import_codevibe_core5.prepareEventTimestamp)({ orderingKey: session.sessionId })
|
|
4081
4467
|
};
|
|
4082
|
-
|
|
4083
|
-
if (!
|
|
4084
|
-
await this.appSyncClient.createEvent(outbound);
|
|
4468
|
+
await this.flushToolActivityAtBoundary(import_codevibe_core5.EventType.INTERACTIVE_PROMPT);
|
|
4469
|
+
if (!await this.createEncryptedEvent(session, input)) return false;
|
|
4085
4470
|
} else {
|
|
4086
4471
|
rec.mobileActionable = false;
|
|
4087
4472
|
if (!await this.emitSuppressedPromptCarrier(session, rec)) return false;
|
|
@@ -4219,6 +4604,14 @@ var McpServer = class _McpServer {
|
|
|
4219
4604
|
}
|
|
4220
4605
|
this.subscription = null;
|
|
4221
4606
|
}
|
|
4607
|
+
try {
|
|
4608
|
+
await this.toolActivity?.prepareForReplacement();
|
|
4609
|
+
} catch (error) {
|
|
4610
|
+
logger.warn("[tool-activity] Failed to settle retired Agy session", {
|
|
4611
|
+
sessionId: old.sessionId,
|
|
4612
|
+
error: String(error)
|
|
4613
|
+
});
|
|
4614
|
+
}
|
|
4222
4615
|
this.session = null;
|
|
4223
4616
|
await this.ensureLaunchSession();
|
|
4224
4617
|
if (this.session) await this.drainE1RetirementPark();
|
|
@@ -4253,10 +4646,10 @@ var McpServer = class _McpServer {
|
|
|
4253
4646
|
}
|
|
4254
4647
|
const result = { ...evt };
|
|
4255
4648
|
try {
|
|
4256
|
-
result.content =
|
|
4649
|
+
result.content = import_codevibe_core5.cryptoService.decryptContent(evt.content, session.sessionKey);
|
|
4257
4650
|
const rawMeta = parseMaybeJson(evt.metadata);
|
|
4258
4651
|
if (rawMeta && typeof rawMeta === "object" && typeof rawMeta.encrypted === "string") {
|
|
4259
|
-
const decryptedMeta =
|
|
4652
|
+
const decryptedMeta = import_codevibe_core5.cryptoService.decryptMetadata(
|
|
4260
4653
|
rawMeta.encrypted,
|
|
4261
4654
|
session.sessionKey
|
|
4262
4655
|
);
|
|
@@ -4335,6 +4728,7 @@ var McpServer = class _McpServer {
|
|
|
4335
4728
|
});
|
|
4336
4729
|
return;
|
|
4337
4730
|
}
|
|
4731
|
+
await this.flushToolActivityAtBoundary(import_codevibe_core5.EventType.INTERACTIVE_PROMPT);
|
|
4338
4732
|
const e1existing = this.e1PromptRaises.get(state.promptId);
|
|
4339
4733
|
const e1rec = e1existing ? e1existing.record : this.newE1Raise(session.sessionId, true);
|
|
4340
4734
|
if (!e1existing) state.promptId = e1rec.promptId;
|
|
@@ -4373,26 +4767,24 @@ var McpServer = class _McpServer {
|
|
|
4373
4767
|
const content = state.paneDisplayHeader || state.matchedPaneHeader || state.pendingCall?.command || state.pendingCall?.filePath || "approval requested";
|
|
4374
4768
|
const input = {
|
|
4375
4769
|
sessionId: session.sessionId,
|
|
4376
|
-
type:
|
|
4377
|
-
source:
|
|
4770
|
+
type: import_codevibe_core5.EventType.INTERACTIVE_PROMPT,
|
|
4771
|
+
source: import_codevibe_core5.EventSource.DESKTOP,
|
|
4378
4772
|
content,
|
|
4379
4773
|
metadata: optionsForMobile,
|
|
4380
4774
|
// E1 (§4) — raise identity top-level: promptId + ownerToken/producerKind/
|
|
4381
4775
|
// mobileActionable (the backend's classifyRaise upserts the open-prompt row).
|
|
4382
|
-
...(0,
|
|
4776
|
+
...(0, import_codevibe_core5.raiseFieldsFromRecord)(e1rec),
|
|
4383
4777
|
// Per-conversation monotonic timestamp (event-timestamp-ordering fix
|
|
4384
4778
|
// v5.4, 2026-05-24). orderingKey scopes the counter to this conv so
|
|
4385
4779
|
// INTERACTIVE_PROMPT can't be forced to lastMs+1 by a newer event
|
|
4386
4780
|
// from a sibling conversation under v9's multi-conv-per-session
|
|
4387
4781
|
// model.
|
|
4388
|
-
timestamp: (0,
|
|
4782
|
+
timestamp: (0, import_codevibe_core5.prepareEventTimestamp)({
|
|
4389
4783
|
orderingKey: `${session.sessionId}:${state.conversationId}`
|
|
4390
4784
|
})
|
|
4391
4785
|
};
|
|
4392
4786
|
try {
|
|
4393
|
-
|
|
4394
|
-
if (!outbound) return;
|
|
4395
|
-
await this.appSyncClient.createEvent(outbound);
|
|
4787
|
+
if (!await this.createEncryptedEvent(session, input)) return;
|
|
4396
4788
|
this.stashE1RaiseContent(
|
|
4397
4789
|
e1rec.promptId,
|
|
4398
4790
|
content,
|
|
@@ -4443,7 +4835,7 @@ var McpServer = class _McpServer {
|
|
|
4443
4835
|
if (shouldDecrypt && sessionKey) {
|
|
4444
4836
|
try {
|
|
4445
4837
|
logger.info("Decrypting attachment", { id: attachment.id });
|
|
4446
|
-
buffer =
|
|
4838
|
+
buffer = import_codevibe_core5.cryptoService.decryptData(buffer, sessionKey);
|
|
4447
4839
|
logger.info("Attachment decrypted successfully", {
|
|
4448
4840
|
id: attachment.id,
|
|
4449
4841
|
decryptedSize: buffer.length
|
|
@@ -4498,7 +4890,7 @@ var McpServer = class _McpServer {
|
|
|
4498
4890
|
await this.ensureLaunchSession();
|
|
4499
4891
|
if (!this.session) return;
|
|
4500
4892
|
const session = this.session;
|
|
4501
|
-
if (evt.source !==
|
|
4893
|
+
if (evt.source !== import_codevibe_core5.EventSource.MOBILE) return;
|
|
4502
4894
|
if (evt.sessionId !== session.sessionId) {
|
|
4503
4895
|
logger.warn("Dropping mobile event with mismatched sessionId", {
|
|
4504
4896
|
expected: session.sessionId,
|
|
@@ -4514,14 +4906,14 @@ var McpServer = class _McpServer {
|
|
|
4514
4906
|
}
|
|
4515
4907
|
evt = decrypted;
|
|
4516
4908
|
await this.markDelivered(evt);
|
|
4517
|
-
if (evt.type ===
|
|
4909
|
+
if (evt.type === import_codevibe_core5.EventType.USER_PROMPT || evt.type === import_codevibe_core5.EventType.PROMPT_RESPONSE) {
|
|
4518
4910
|
const mobileFloorMs = Date.parse(evt.timestamp);
|
|
4519
4911
|
this.mobilePromptFloorBySession.set(session.sessionId, {
|
|
4520
4912
|
floorMs: Number.isNaN(mobileFloorMs) ? Date.now() : mobileFloorMs,
|
|
4521
4913
|
setAtWallMs: Date.now()
|
|
4522
4914
|
});
|
|
4523
4915
|
}
|
|
4524
|
-
if (evt.type ===
|
|
4916
|
+
if (evt.type === import_codevibe_core5.EventType.USER_PROMPT || evt.type === import_codevibe_core5.EventType.PROMPT_RESPONSE) {
|
|
4525
4917
|
let promptContent = evt.content;
|
|
4526
4918
|
const attachments = evt.attachments ?? [];
|
|
4527
4919
|
if (attachments.length > 0) {
|
|
@@ -4560,16 +4952,16 @@ var McpServer = class _McpServer {
|
|
|
4560
4952
|
const rawMeta = parseMaybeJson(evt.metadata);
|
|
4561
4953
|
const meta = rawMeta && typeof rawMeta === "object" ? rawMeta : {};
|
|
4562
4954
|
const evtAny = evt;
|
|
4563
|
-
const responsePromptId = evt.type ===
|
|
4955
|
+
const responsePromptId = evt.type === import_codevibe_core5.EventType.PROMPT_RESPONSE ? typeof meta.promptId === "string" && meta.promptId || typeof meta.prompt_id === "string" && meta.prompt_id || typeof evtAny.promptId === "string" && evtAny.promptId || typeof evtAny.prompt_id === "string" && evtAny.prompt_id || null : null;
|
|
4564
4956
|
const advertisedOptionNumbers = Array.from(new Set(
|
|
4565
4957
|
pendingPrompts.flatMap((prompt) => Object.keys(prompt.submitMap))
|
|
4566
4958
|
));
|
|
4567
|
-
const intent = (0,
|
|
4959
|
+
const intent = (0, import_codevibe_core5.classifyMobilePromptInput)(
|
|
4568
4960
|
promptContent,
|
|
4569
4961
|
advertisedOptionNumbers
|
|
4570
4962
|
);
|
|
4571
4963
|
if (intent.kind === "option") {
|
|
4572
|
-
const activePrompt = evt.type ===
|
|
4964
|
+
const activePrompt = evt.type === import_codevibe_core5.EventType.PROMPT_RESPONSE ? pendingPrompts.find((prompt) => prompt.promptId === responsePromptId) ?? null : pendingPrompts.length === 1 ? pendingPrompts[0] : null;
|
|
4573
4965
|
if (!activePrompt || !Object.prototype.hasOwnProperty.call(activePrompt.submitMap, intent.option)) {
|
|
4574
4966
|
logger.warn("Numeric input does not identify one current advertised prompt; dropping", {
|
|
4575
4967
|
sessionId: session.sessionId,
|
|
@@ -4579,7 +4971,7 @@ var McpServer = class _McpServer {
|
|
|
4579
4971
|
});
|
|
4580
4972
|
return;
|
|
4581
4973
|
}
|
|
4582
|
-
if (evt.type ===
|
|
4974
|
+
if (evt.type === import_codevibe_core5.EventType.PROMPT_RESPONSE) {
|
|
4583
4975
|
if (responsePromptId !== activePrompt.promptId) {
|
|
4584
4976
|
logger.warn("PROMPT_RESPONSE does not identify the current advertised prompt; dropping", {
|
|
4585
4977
|
sessionId: session.sessionId,
|
|
@@ -4631,16 +5023,15 @@ var McpServer = class _McpServer {
|
|
|
4631
5023
|
async emitPromptSafetyNotification(session, content) {
|
|
4632
5024
|
const input = {
|
|
4633
5025
|
sessionId: session.sessionId,
|
|
4634
|
-
type:
|
|
4635
|
-
source:
|
|
5026
|
+
type: import_codevibe_core5.EventType.NOTIFICATION,
|
|
5027
|
+
source: import_codevibe_core5.EventSource.DESKTOP,
|
|
4636
5028
|
content,
|
|
4637
5029
|
metadata: { promptSafetyBlocked: true },
|
|
4638
|
-
timestamp: (0,
|
|
5030
|
+
timestamp: (0, import_codevibe_core5.prepareEventTimestamp)({ orderingKey: session.sessionId })
|
|
4639
5031
|
};
|
|
4640
|
-
|
|
4641
|
-
if (!outbound) return;
|
|
5032
|
+
await this.flushToolActivityAtBoundary(import_codevibe_core5.EventType.NOTIFICATION);
|
|
4642
5033
|
try {
|
|
4643
|
-
await this.
|
|
5034
|
+
await this.createEncryptedEvent(session, input);
|
|
4644
5035
|
} catch (error) {
|
|
4645
5036
|
logger.warn("Failed to emit prompt-safety notification", { error: String(error) });
|
|
4646
5037
|
}
|
|
@@ -4659,7 +5050,7 @@ var McpServer = class _McpServer {
|
|
|
4659
5050
|
eventId: evt.eventId,
|
|
4660
5051
|
sessionId: evt.sessionId,
|
|
4661
5052
|
timestamp: evt.timestamp,
|
|
4662
|
-
deliveryStatus:
|
|
5053
|
+
deliveryStatus: import_codevibe_core5.DeliveryStatus.DELIVERED
|
|
4663
5054
|
});
|
|
4664
5055
|
} catch (err) {
|
|
4665
5056
|
logger.warn("updateEventStatus(DELIVERED) failed", {
|
|
@@ -4683,7 +5074,7 @@ var McpServer = class _McpServer {
|
|
|
4683
5074
|
eventId: evt.eventId,
|
|
4684
5075
|
sessionId: evt.sessionId,
|
|
4685
5076
|
timestamp: evt.timestamp,
|
|
4686
|
-
deliveryStatus:
|
|
5077
|
+
deliveryStatus: import_codevibe_core5.DeliveryStatus.EXECUTED
|
|
4687
5078
|
});
|
|
4688
5079
|
} catch (err) {
|
|
4689
5080
|
logger.warn("updateEventStatus(EXECUTED) failed", {
|
|
@@ -4711,15 +5102,14 @@ var McpServer = class _McpServer {
|
|
|
4711
5102
|
// orderingKey is session-scoped. Slightly coarser than the
|
|
4712
5103
|
// per-conv key used by transcript emits, but acceptable because
|
|
4713
5104
|
// hook-script pushes are off the hot transcript path.
|
|
4714
|
-
timestamp: (0,
|
|
5105
|
+
timestamp: (0, import_codevibe_core5.prepareEventTimestamp)({ orderingKey: payload.sessionId })
|
|
4715
5106
|
};
|
|
4716
|
-
const
|
|
4717
|
-
if (!
|
|
5107
|
+
const created = await this.createEncryptedEvent(session, input);
|
|
5108
|
+
if (!created) {
|
|
4718
5109
|
throw new Error(
|
|
4719
5110
|
`fail-closed: encrypted session ${payload.sessionId} has no session key \u2014 event dropped (#638)`
|
|
4720
5111
|
);
|
|
4721
5112
|
}
|
|
4722
|
-
const created = await this.appSyncClient.createEvent(finalInput);
|
|
4723
5113
|
return { eventId: created.eventId };
|
|
4724
5114
|
}
|
|
4725
5115
|
findSessionByBackendId(backendSessionId) {
|
|
@@ -4950,7 +5340,7 @@ async function main() {
|
|
|
4950
5340
|
tmuxTarget: process.env.CODEVIBE_AGY_TMUX_TARGET ?? void 0,
|
|
4951
5341
|
wrapperPid: process.env.CODEVIBE_AGY_WRAPPER_PID ? parseInt(process.env.CODEVIBE_AGY_WRAPPER_PID, 10) : void 0
|
|
4952
5342
|
});
|
|
4953
|
-
(0,
|
|
5343
|
+
(0, import_codevibe_core5.installDaemonProcessGuards)(logger, {
|
|
4954
5344
|
onFatal: () => {
|
|
4955
5345
|
void server.stop().finally(() => process.exit(1));
|
|
4956
5346
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-antigravity-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.19",
|
|
4
4
|
"description": "Control Antigravity CLI from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"codevibe": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=22.0.0"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@quantiya/codevibe-core": "2.0.
|
|
51
|
+
"@quantiya/codevibe-core": "2.0.17",
|
|
52
52
|
"chokidar": "^5.0.0",
|
|
53
53
|
"dotenv": "^16.6.1",
|
|
54
54
|
"express": "^5.1.0",
|