@triflux/core 10.25.1 → 10.26.0
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/hooks/hook-registry.json +1 -1
- package/hooks/keyword-rules.json +8 -8
- package/hooks/safety-guard.mjs +8 -1
- package/hub/bridge.mjs +221 -1
- package/hub/delegator/contracts.mjs +6 -1
- package/hub/delegator/schema/delegator-tools.schema.json +1 -1
- package/hub/delegator/service.mjs +7 -2
- package/hub/gemini-adapter.mjs +27 -35
- package/hub/routing/q-learning.mjs +3 -3
- package/hub/team/claude-daemon-control.mjs +1130 -0
- package/hub/team/retry-state-machine.mjs +83 -14
- package/hub/workers/worker-utils.mjs +1 -1
- package/hud/constants.mjs +6 -0
- package/hud/hud-qos-status.mjs +14 -10
- package/hud/providers/gemini.mjs +26 -1
- package/hud/renderers.mjs +27 -4
- package/package.json +1 -1
- package/scripts/lib/cli-codex.mjs +3 -3
- package/scripts/lib/cli-gemini.mjs +20 -50
- package/scripts/lib/keyword-rules.mjs +1 -1
- package/scripts/lib/psmux-info.mjs +50 -5
package/hooks/hook-registry.json
CHANGED
package/hooks/keyword-rules.json
CHANGED
|
@@ -347,7 +347,7 @@
|
|
|
347
347
|
"supersedes": [],
|
|
348
348
|
"exclusive": false,
|
|
349
349
|
"state": null,
|
|
350
|
-
"mcp_route": "
|
|
350
|
+
"mcp_route": "antigravity"
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
353
|
"id": "chrome-route",
|
|
@@ -366,7 +366,7 @@
|
|
|
366
366
|
"supersedes": [],
|
|
367
367
|
"exclusive": false,
|
|
368
368
|
"state": null,
|
|
369
|
-
"mcp_route": "
|
|
369
|
+
"mcp_route": "antigravity"
|
|
370
370
|
},
|
|
371
371
|
{
|
|
372
372
|
"id": "jira-route",
|
|
@@ -404,7 +404,7 @@
|
|
|
404
404
|
"supersedes": [],
|
|
405
405
|
"exclusive": false,
|
|
406
406
|
"state": null,
|
|
407
|
-
"mcp_route": "
|
|
407
|
+
"mcp_route": "antigravity"
|
|
408
408
|
},
|
|
409
409
|
{
|
|
410
410
|
"id": "calendar-route",
|
|
@@ -423,7 +423,7 @@
|
|
|
423
423
|
"supersedes": [],
|
|
424
424
|
"exclusive": false,
|
|
425
425
|
"state": null,
|
|
426
|
-
"mcp_route": "
|
|
426
|
+
"mcp_route": "antigravity"
|
|
427
427
|
},
|
|
428
428
|
{
|
|
429
429
|
"id": "playwright-route",
|
|
@@ -438,7 +438,7 @@
|
|
|
438
438
|
"supersedes": [],
|
|
439
439
|
"exclusive": false,
|
|
440
440
|
"state": null,
|
|
441
|
-
"mcp_route": "
|
|
441
|
+
"mcp_route": "antigravity"
|
|
442
442
|
},
|
|
443
443
|
{
|
|
444
444
|
"id": "canva-route",
|
|
@@ -453,7 +453,7 @@
|
|
|
453
453
|
"supersedes": [],
|
|
454
454
|
"exclusive": false,
|
|
455
455
|
"state": null,
|
|
456
|
-
"mcp_route": "
|
|
456
|
+
"mcp_route": "antigravity"
|
|
457
457
|
},
|
|
458
458
|
{
|
|
459
459
|
"id": "confluence-route",
|
|
@@ -472,7 +472,7 @@
|
|
|
472
472
|
"supersedes": [],
|
|
473
473
|
"exclusive": false,
|
|
474
474
|
"state": null,
|
|
475
|
-
"mcp_route": "
|
|
475
|
+
"mcp_route": "antigravity"
|
|
476
476
|
},
|
|
477
477
|
{
|
|
478
478
|
"id": "slack-route",
|
|
@@ -491,7 +491,7 @@
|
|
|
491
491
|
"supersedes": [],
|
|
492
492
|
"exclusive": false,
|
|
493
493
|
"state": null,
|
|
494
|
-
"mcp_route": "
|
|
494
|
+
"mcp_route": "antigravity"
|
|
495
495
|
},
|
|
496
496
|
{
|
|
497
497
|
"id": "github-route",
|
package/hooks/safety-guard.mjs
CHANGED
|
@@ -340,7 +340,14 @@ function main() {
|
|
|
340
340
|
return hasSegmentInvocation(cmd, WT_DIRECT_PATTERNS);
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
|
|
343
|
+
// wt.exe는 Windows Terminal 전용. macOS/Linux에서는 PATH에 존재하지 않아
|
|
344
|
+
// 차단해도 dormant이며, 문서/로그 텍스트의 wt 단어가 false positive로 잡힐
|
|
345
|
+
// 위험만 있다. win32에서만 차단 적용.
|
|
346
|
+
// SAFETY_GUARD_FORCE_PLATFORM env로 테스트에서 platform 시뮬레이션 가능
|
|
347
|
+
// (mac CI에서 win32 차단 동작을 검증하기 위함).
|
|
348
|
+
const guardPlatform =
|
|
349
|
+
process.env.SAFETY_GUARD_FORCE_PLATFORM || process.platform;
|
|
350
|
+
if (guardPlatform === "win32" && isWtDirectInvocation(command)) {
|
|
344
351
|
blockCommand(WT_DIRECT_BLOCK_MESSAGE, command);
|
|
345
352
|
}
|
|
346
353
|
|
package/hub/bridge.mjs
CHANGED
|
@@ -371,6 +371,7 @@ export function parseArgs(argv) {
|
|
|
371
371
|
capabilities: { type: "string" },
|
|
372
372
|
file: { type: "string" },
|
|
373
373
|
payload: { type: "string" },
|
|
374
|
+
"payload-file": { type: "string" },
|
|
374
375
|
topic: { type: "string" },
|
|
375
376
|
trace: { type: "string" },
|
|
376
377
|
correlation: { type: "string" },
|
|
@@ -448,6 +449,21 @@ export function parseJsonSafe(raw, fallback = null) {
|
|
|
448
449
|
}
|
|
449
450
|
}
|
|
450
451
|
|
|
452
|
+
export function readBridgePayload(args = {}, stdin) {
|
|
453
|
+
if (args["payload-file"]) {
|
|
454
|
+
const payloadFile = String(args["payload-file"]);
|
|
455
|
+
const raw =
|
|
456
|
+
payloadFile === "-"
|
|
457
|
+
? stdin === undefined
|
|
458
|
+
? readFileSync(0, "utf8")
|
|
459
|
+
: String(stdin)
|
|
460
|
+
: readFileSync(payloadFile, "utf8");
|
|
461
|
+
return JSON.parse(raw);
|
|
462
|
+
}
|
|
463
|
+
if (args.payload) return JSON.parse(args.payload);
|
|
464
|
+
return {};
|
|
465
|
+
}
|
|
466
|
+
|
|
451
467
|
function normalizeBridgePayload(payload) {
|
|
452
468
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
453
469
|
return {};
|
|
@@ -1145,6 +1161,185 @@ async function cmdHitlPending() {
|
|
|
1145
1161
|
return emitJson(outcome?.result || unavailableResult());
|
|
1146
1162
|
}
|
|
1147
1163
|
|
|
1164
|
+
async function loadDaemonControl() {
|
|
1165
|
+
return await import("./team/claude-daemon-control.mjs");
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
function daemonErrorResult(error) {
|
|
1169
|
+
return {
|
|
1170
|
+
ok: false,
|
|
1171
|
+
error: error?.message || String(error),
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
function daemonAttachErrorResult(error) {
|
|
1176
|
+
return {
|
|
1177
|
+
ok: false,
|
|
1178
|
+
text: "",
|
|
1179
|
+
raw: "",
|
|
1180
|
+
responseRaw: "",
|
|
1181
|
+
matchedCompletion: false,
|
|
1182
|
+
timedOut: false,
|
|
1183
|
+
closed: false,
|
|
1184
|
+
inputSent: error?.inputSent === true,
|
|
1185
|
+
error: error?.message || String(error),
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
function daemonInterruptErrorResult(error) {
|
|
1190
|
+
return {
|
|
1191
|
+
ok: false,
|
|
1192
|
+
done: false,
|
|
1193
|
+
aborted: false,
|
|
1194
|
+
reason: "interrupt_failed",
|
|
1195
|
+
inputSent: error?.inputSent === true,
|
|
1196
|
+
error: error?.message || String(error),
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
function numericOption(value, fallback) {
|
|
1201
|
+
const parsed = Number(value);
|
|
1202
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
async function cmdDaemonProbe(args) {
|
|
1206
|
+
try {
|
|
1207
|
+
const payload = readBridgePayload(args);
|
|
1208
|
+
const {
|
|
1209
|
+
deriveClaudeDaemonPaths,
|
|
1210
|
+
findDaemonJobBySessionId,
|
|
1211
|
+
findDaemonJobByShort,
|
|
1212
|
+
sendClaudeControlRequest,
|
|
1213
|
+
} = await loadDaemonControl();
|
|
1214
|
+
const daemonPaths = deriveClaudeDaemonPaths({
|
|
1215
|
+
configDir: payload.configDir,
|
|
1216
|
+
});
|
|
1217
|
+
const list = await sendClaudeControlRequest(
|
|
1218
|
+
daemonPaths.controlSock,
|
|
1219
|
+
{ proto: 1, op: "list" },
|
|
1220
|
+
{ timeoutMs: numericOption(payload.timeoutMs, 6000) },
|
|
1221
|
+
);
|
|
1222
|
+
const target = payload.sessionId
|
|
1223
|
+
? findDaemonJobBySessionId(list, payload.sessionId)
|
|
1224
|
+
: payload.short
|
|
1225
|
+
? findDaemonJobByShort(list, payload.short)
|
|
1226
|
+
: null;
|
|
1227
|
+
|
|
1228
|
+
return emitJson({
|
|
1229
|
+
ok: list?.ok !== false,
|
|
1230
|
+
controlSock: daemonPaths.controlSock,
|
|
1231
|
+
sessions: list?.jobs || list?.sessions || [],
|
|
1232
|
+
target: target || undefined,
|
|
1233
|
+
error: list?.ok === false ? list?.error : undefined,
|
|
1234
|
+
});
|
|
1235
|
+
} catch (error) {
|
|
1236
|
+
return emitJson(daemonErrorResult(error));
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
async function cmdDaemonAttach(args) {
|
|
1241
|
+
try {
|
|
1242
|
+
const payload = readBridgePayload(args);
|
|
1243
|
+
if (!payload.prompt) throw new Error("prompt is required");
|
|
1244
|
+
|
|
1245
|
+
const {
|
|
1246
|
+
attachClaudeDaemonSession,
|
|
1247
|
+
deriveClaudeDaemonPaths,
|
|
1248
|
+
findDaemonJobBySessionId,
|
|
1249
|
+
sendClaudeControlRequest,
|
|
1250
|
+
} = await loadDaemonControl();
|
|
1251
|
+
const daemonPaths = deriveClaudeDaemonPaths({
|
|
1252
|
+
configDir: payload.configDir,
|
|
1253
|
+
});
|
|
1254
|
+
let short = payload.short;
|
|
1255
|
+
if (!short && payload.sessionId) {
|
|
1256
|
+
const list = await sendClaudeControlRequest(
|
|
1257
|
+
daemonPaths.controlSock,
|
|
1258
|
+
{ proto: 1, op: "list" },
|
|
1259
|
+
{ timeoutMs: numericOption(payload.timeoutMs, 6000) },
|
|
1260
|
+
);
|
|
1261
|
+
const target = findDaemonJobBySessionId(list, payload.sessionId);
|
|
1262
|
+
short = target?.short;
|
|
1263
|
+
}
|
|
1264
|
+
if (!short) throw new Error("short or resolvable sessionId is required");
|
|
1265
|
+
|
|
1266
|
+
const result = await attachClaudeDaemonSession({
|
|
1267
|
+
controlSock: daemonPaths.controlSock,
|
|
1268
|
+
short,
|
|
1269
|
+
input: payload.prompt,
|
|
1270
|
+
cols: numericOption(payload.cols, undefined),
|
|
1271
|
+
rows: numericOption(payload.rows, undefined),
|
|
1272
|
+
timeoutMs: numericOption(payload.timeoutMs, 30_000),
|
|
1273
|
+
});
|
|
1274
|
+
|
|
1275
|
+
return emitJson({
|
|
1276
|
+
ok: result.matchedCompletion === true,
|
|
1277
|
+
text: result.text,
|
|
1278
|
+
raw: result.streamText,
|
|
1279
|
+
responseRaw: result.responseStreamText,
|
|
1280
|
+
matchedCompletion: result.matchedCompletion === true,
|
|
1281
|
+
timedOut: result.timedOut === true,
|
|
1282
|
+
closed: result.closed === true,
|
|
1283
|
+
inputSent: result.inputSent === true,
|
|
1284
|
+
error:
|
|
1285
|
+
result.handshake?.ok === false ? result.handshake?.error : undefined,
|
|
1286
|
+
});
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
return emitJson(daemonAttachErrorResult(error));
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
async function cmdDaemonInterrupt(args) {
|
|
1293
|
+
try {
|
|
1294
|
+
const payload = readBridgePayload(args);
|
|
1295
|
+
|
|
1296
|
+
const {
|
|
1297
|
+
deriveClaudeDaemonPaths,
|
|
1298
|
+
findDaemonJobBySessionId,
|
|
1299
|
+
interruptClaudeDaemonSession,
|
|
1300
|
+
sendClaudeControlRequest,
|
|
1301
|
+
} = await loadDaemonControl();
|
|
1302
|
+
const daemonPaths = deriveClaudeDaemonPaths({
|
|
1303
|
+
configDir: payload.configDir,
|
|
1304
|
+
});
|
|
1305
|
+
let short = payload.short;
|
|
1306
|
+
if (!short && payload.sessionId) {
|
|
1307
|
+
const list = await sendClaudeControlRequest(
|
|
1308
|
+
daemonPaths.controlSock,
|
|
1309
|
+
{ proto: 1, op: "list" },
|
|
1310
|
+
{ timeoutMs: numericOption(payload.timeoutMs, 6000) },
|
|
1311
|
+
);
|
|
1312
|
+
const target = findDaemonJobBySessionId(list, payload.sessionId);
|
|
1313
|
+
short = target?.short;
|
|
1314
|
+
}
|
|
1315
|
+
if (!short) throw new Error("short or resolvable sessionId is required");
|
|
1316
|
+
|
|
1317
|
+
const result = await interruptClaudeDaemonSession({
|
|
1318
|
+
controlSock: daemonPaths.controlSock,
|
|
1319
|
+
short,
|
|
1320
|
+
cols: numericOption(payload.cols, undefined),
|
|
1321
|
+
rows: numericOption(payload.rows, undefined),
|
|
1322
|
+
timeoutMs: numericOption(payload.timeoutMs, 5000),
|
|
1323
|
+
});
|
|
1324
|
+
const aborted = result.inputSent === true;
|
|
1325
|
+
|
|
1326
|
+
return emitJson({
|
|
1327
|
+
ok: aborted,
|
|
1328
|
+
done: false,
|
|
1329
|
+
aborted,
|
|
1330
|
+
reason: aborted ? "user_interrupt" : "interrupt_failed",
|
|
1331
|
+
raw: result.streamText,
|
|
1332
|
+
timedOut: result.timedOut === true,
|
|
1333
|
+
closed: result.closed === true,
|
|
1334
|
+
inputSent: result.inputSent === true,
|
|
1335
|
+
error:
|
|
1336
|
+
result.handshake?.ok === false ? result.handshake?.error : undefined,
|
|
1337
|
+
});
|
|
1338
|
+
} catch (error) {
|
|
1339
|
+
return emitJson(daemonInterruptErrorResult(error));
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1148
1343
|
// ---------------------------------------------------------------------------
|
|
1149
1344
|
// retry-run / retry-status — Phase 3 Step C2 bridge 서브커맨드.
|
|
1150
1345
|
// retry-state-machine.mjs 를 multi-process safe 하게 외부 호출용 wrap.
|
|
@@ -1178,6 +1373,21 @@ function buildRetrySmFromArgs(args, snapshot) {
|
|
|
1178
1373
|
return sm;
|
|
1179
1374
|
}
|
|
1180
1375
|
|
|
1376
|
+
function buildRetryCliInvocation(cli) {
|
|
1377
|
+
if (!cli) return null;
|
|
1378
|
+
|
|
1379
|
+
const invocation = {
|
|
1380
|
+
cli: cli.cli,
|
|
1381
|
+
model: cli.model,
|
|
1382
|
+
argv: [],
|
|
1383
|
+
};
|
|
1384
|
+
if (cli.profile) invocation.profile = cli.profile;
|
|
1385
|
+
if (cli.cli === "codex" && cli.profile) {
|
|
1386
|
+
invocation.argv.push("--profile", cli.profile);
|
|
1387
|
+
}
|
|
1388
|
+
return invocation;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1181
1391
|
async function cmdRetryRun(args) {
|
|
1182
1392
|
const snapshotFile = args.snapshot || args["snapshot-file"];
|
|
1183
1393
|
const event = args.event;
|
|
@@ -1216,12 +1426,14 @@ async function cmdRetryRun(args) {
|
|
|
1216
1426
|
|
|
1217
1427
|
const terminal = ["DONE", "STUCK", "BUDGET_EXCEEDED"].includes(snap.current);
|
|
1218
1428
|
const cli = snap.cliChain?.[snap.cliIndex] || null;
|
|
1429
|
+
const cliInvocation = buildRetryCliInvocation(cli);
|
|
1219
1430
|
const out = {
|
|
1220
1431
|
ok: true,
|
|
1221
1432
|
current: snap.current,
|
|
1222
1433
|
iterations: snap.iterations,
|
|
1223
1434
|
cliIndex: snap.cliIndex,
|
|
1224
1435
|
cli,
|
|
1436
|
+
cliInvocation,
|
|
1225
1437
|
done: snap.current === "DONE",
|
|
1226
1438
|
shouldStop: terminal,
|
|
1227
1439
|
stuckCounter: snap.stuckCounter,
|
|
@@ -1245,6 +1457,7 @@ async function cmdRetryStatus(args) {
|
|
|
1245
1457
|
}
|
|
1246
1458
|
const terminal = ["DONE", "STUCK", "BUDGET_EXCEEDED"].includes(snap.current);
|
|
1247
1459
|
const cli = snap.cliChain?.[snap.cliIndex] || null;
|
|
1460
|
+
const cliInvocation = buildRetryCliInvocation(cli);
|
|
1248
1461
|
console.log(
|
|
1249
1462
|
JSON.stringify({
|
|
1250
1463
|
ok: true,
|
|
@@ -1254,6 +1467,7 @@ async function cmdRetryStatus(args) {
|
|
|
1254
1467
|
maxIterations: snap.maxIterations,
|
|
1255
1468
|
cliIndex: snap.cliIndex,
|
|
1256
1469
|
cli,
|
|
1470
|
+
cliInvocation,
|
|
1257
1471
|
mode: snap.mode,
|
|
1258
1472
|
shouldStop: terminal,
|
|
1259
1473
|
stuckCounter: snap.stuckCounter,
|
|
@@ -1322,13 +1536,19 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1322
1536
|
return await cmdHitlSubmit(args);
|
|
1323
1537
|
case "hitl-pending":
|
|
1324
1538
|
return await cmdHitlPending(args);
|
|
1539
|
+
case "daemon-probe":
|
|
1540
|
+
return await cmdDaemonProbe(args);
|
|
1541
|
+
case "daemon-attach":
|
|
1542
|
+
return await cmdDaemonAttach(args);
|
|
1543
|
+
case "daemon-interrupt":
|
|
1544
|
+
return await cmdDaemonInterrupt(args);
|
|
1325
1545
|
case "retry-run":
|
|
1326
1546
|
return await cmdRetryRun(args);
|
|
1327
1547
|
case "retry-status":
|
|
1328
1548
|
return await cmdRetryStatus(args);
|
|
1329
1549
|
default:
|
|
1330
1550
|
console.error(
|
|
1331
|
-
"사용법: bridge.mjs <register|result|control|handoff|publish|send-input|context|deregister|assign-async|assign-result|assign-status|assign-retry|team-info|team-task-list|team-task-update|team-send-message|pipeline-state|pipeline-advance|pipeline-init|pipeline-list|ping|delegator-delegate|delegator-reply|delegator-status|hitl-request|hitl-submit|hitl-pending|retry-run|retry-status> [--옵션]",
|
|
1551
|
+
"사용법: bridge.mjs <register|result|control|handoff|publish|send-input|context|deregister|assign-async|assign-result|assign-status|assign-retry|team-info|team-task-list|team-task-update|team-send-message|pipeline-state|pipeline-advance|pipeline-init|pipeline-list|ping|delegator-delegate|delegator-reply|delegator-status|hitl-request|hitl-submit|hitl-pending|daemon-probe|daemon-attach|daemon-interrupt|retry-run|retry-status> [--옵션]",
|
|
1332
1552
|
);
|
|
1333
1553
|
process.exit(1);
|
|
1334
1554
|
}
|
|
@@ -25,7 +25,12 @@ export const DELEGATOR_JOB_STATUSES = Object.freeze([
|
|
|
25
25
|
|
|
26
26
|
export const DELEGATOR_MODES = Object.freeze(["sync", "async"]);
|
|
27
27
|
|
|
28
|
-
export const DELEGATOR_PROVIDERS = Object.freeze([
|
|
28
|
+
export const DELEGATOR_PROVIDERS = Object.freeze([
|
|
29
|
+
"auto",
|
|
30
|
+
"codex",
|
|
31
|
+
"antigravity",
|
|
32
|
+
"gemini",
|
|
33
|
+
]);
|
|
29
34
|
|
|
30
35
|
export const DELEGATOR_SCHEMA_URL = new URL(
|
|
31
36
|
"./schema/delegator-tools.schema.json",
|
|
@@ -19,6 +19,11 @@ function assertKnown(enumValues, value, fieldName) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function normalizeProvider(provider = "auto") {
|
|
23
|
+
const value = provider || "auto";
|
|
24
|
+
return value === "gemini" ? "antigravity" : value;
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
export class DelegatorService {
|
|
23
28
|
constructor({
|
|
24
29
|
idFactory = randomUUID,
|
|
@@ -40,7 +45,7 @@ export class DelegatorService {
|
|
|
40
45
|
const timestamp = this.now().toISOString();
|
|
41
46
|
const jobId = input.job_id || this.idFactory();
|
|
42
47
|
const mode = input.mode || "sync";
|
|
43
|
-
const providerRequested = input.provider
|
|
48
|
+
const providerRequested = normalizeProvider(input.provider);
|
|
44
49
|
|
|
45
50
|
assertKnown(DELEGATOR_MODES, mode, "mode");
|
|
46
51
|
assertKnown(DELEGATOR_PROVIDERS, providerRequested, "provider");
|
|
@@ -86,7 +91,7 @@ export class DelegatorService {
|
|
|
86
91
|
_normalizeInput(input = {}) {
|
|
87
92
|
return {
|
|
88
93
|
prompt: input.prompt,
|
|
89
|
-
provider: input.provider
|
|
94
|
+
provider: normalizeProvider(input.provider),
|
|
90
95
|
mode: input.mode || "sync",
|
|
91
96
|
agent_type: input.agent_type || input.agentType || "executor",
|
|
92
97
|
cwd: input.cwd || null,
|
package/hub/gemini-adapter.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// hub/gemini-adapter.mjs — Gemini
|
|
2
|
-
//
|
|
1
|
+
// hub/gemini-adapter.mjs — legacy Gemini adapter compatibility layer
|
|
2
|
+
// The "gemini" route is retained as an alias, but execution is Antigravity/agy.
|
|
3
3
|
|
|
4
4
|
import { mkdirSync } from "node:fs";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
@@ -8,11 +8,14 @@ import {
|
|
|
8
8
|
executeWithCircuitBroker,
|
|
9
9
|
normalizePathForShell,
|
|
10
10
|
runProcess,
|
|
11
|
-
shellQuote,
|
|
12
11
|
} from "./cli-adapter-base.mjs";
|
|
13
12
|
import { whichCommandAsync } from "./platform.mjs";
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
function shellSingleQuote(value) {
|
|
15
|
+
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ── Antigravity stall inference ─────────────────────────────────
|
|
16
19
|
|
|
17
20
|
function inferStallMode(stdout, stderr) {
|
|
18
21
|
const text = `${stdout}\n${stderr}`.toLowerCase();
|
|
@@ -30,12 +33,12 @@ function inferStallMode(stdout, stderr) {
|
|
|
30
33
|
// ── Preflight ───────────────────────────────────────────────────
|
|
31
34
|
|
|
32
35
|
async function runPreflight(opts = {}) {
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
36
|
+
const agyPath = await whichCommandAsync("agy");
|
|
37
|
+
if (!agyPath) {
|
|
35
38
|
return {
|
|
36
|
-
|
|
39
|
+
agyPath: null,
|
|
37
40
|
warnings: [
|
|
38
|
-
"
|
|
41
|
+
"Antigravity CLI not found. Install Antigravity and ensure `agy` is available on PATH.",
|
|
39
42
|
],
|
|
40
43
|
excludeMcpServers: [],
|
|
41
44
|
ok: false,
|
|
@@ -48,40 +51,27 @@ async function runPreflight(opts = {}) {
|
|
|
48
51
|
for (const name of Array.isArray(opts.mcpServers) ? opts.mcpServers : []) {
|
|
49
52
|
const server = String(name ?? "").trim();
|
|
50
53
|
if (!server) continue;
|
|
51
|
-
//
|
|
52
|
-
// 사전 probe는 수행하지 않음 (gemini가 자체적으로 graceful degrade)
|
|
54
|
+
// Antigravity MCP health는 best-effort: 실행 시점 probe는 수행하지 않음.
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
return {
|
|
57
|
+
return { agyPath, warnings, excludeMcpServers, ok: true };
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
// ── Command building ────────────────────────────────────────────
|
|
59
61
|
|
|
60
62
|
function buildGeminiCommand(prompt, resultFile, opts = {}) {
|
|
61
|
-
const parts = [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
? opts.excludeMcpServers
|
|
71
|
-
: [];
|
|
72
|
-
const filtered = allowed.filter((name) => !excluded.includes(name));
|
|
73
|
-
if (filtered.length) {
|
|
74
|
-
parts.push(
|
|
75
|
-
"--allowed-mcp-server-names",
|
|
76
|
-
...filtered.map((n) => shellQuote(n)),
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
parts.push("--prompt", shellQuote(prompt));
|
|
81
|
-
parts.push("--output-format", "text");
|
|
63
|
+
const parts = [
|
|
64
|
+
"printf",
|
|
65
|
+
"%s",
|
|
66
|
+
shellSingleQuote(prompt),
|
|
67
|
+
"|",
|
|
68
|
+
"agy",
|
|
69
|
+
"--print",
|
|
70
|
+
"--dangerously-skip-permissions",
|
|
71
|
+
];
|
|
82
72
|
|
|
83
73
|
if (resultFile) {
|
|
84
|
-
return `${parts.join(" ")} > ${
|
|
74
|
+
return `${parts.join(" ")} > ${shellSingleQuote(normalizePathForShell(resultFile))} 2>${shellSingleQuote(normalizePathForShell(resultFile + ".err"))}`;
|
|
85
75
|
}
|
|
86
76
|
|
|
87
77
|
return parts.join(" ");
|
|
@@ -114,11 +104,11 @@ export function buildExecArgs(opts = {}) {
|
|
|
114
104
|
// ── Execution ───────────────────────────────────────────────────
|
|
115
105
|
|
|
116
106
|
async function runGemini(prompt, workdir, preflight, attempt, lease) {
|
|
117
|
-
const dir = join(tmpdir(), "triflux-
|
|
107
|
+
const dir = join(tmpdir(), "triflux-antigravity-exec");
|
|
118
108
|
mkdirSync(dir, { recursive: true });
|
|
119
109
|
const resultFile = join(
|
|
120
110
|
dir,
|
|
121
|
-
`
|
|
111
|
+
`agy-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`,
|
|
122
112
|
);
|
|
123
113
|
const command = buildGeminiCommand(prompt, resultFile, {
|
|
124
114
|
model: attempt.model,
|
|
@@ -153,6 +143,8 @@ export async function getCircuitState() {
|
|
|
153
143
|
|
|
154
144
|
export function execute(opts = {}) {
|
|
155
145
|
return executeWithCircuitBroker({
|
|
146
|
+
// Account-broker still stores this compatibility bucket as "gemini";
|
|
147
|
+
// execution itself is routed to agy above.
|
|
156
148
|
provider: "gemini",
|
|
157
149
|
runFn: runGemini,
|
|
158
150
|
preflightFn: (o) => runPreflight({ mcpServers: o.mcpServers }),
|
|
@@ -20,7 +20,7 @@ try {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** 사용 가능한 CLI 액션 */
|
|
23
|
-
const ACTIONS = ["codex", "
|
|
23
|
+
const ACTIONS = ["codex", "antigravity", "claude", "haiku", "sonnet"];
|
|
24
24
|
|
|
25
25
|
/** 특성 벡터 키워드 (48차원, 에이전트 타입 기반) */
|
|
26
26
|
const FEATURE_KEYWORDS = [
|
|
@@ -42,7 +42,7 @@ const FEATURE_KEYWORDS = [
|
|
|
42
42
|
"optimize",
|
|
43
43
|
"research",
|
|
44
44
|
"evaluate",
|
|
45
|
-
// 디자인/문서 (
|
|
45
|
+
// 디자인/문서 (Antigravity 친화)
|
|
46
46
|
"design",
|
|
47
47
|
"ui",
|
|
48
48
|
"ux",
|
|
@@ -74,7 +74,7 @@ const FEATURE_KEYWORDS = [
|
|
|
74
74
|
"검토",
|
|
75
75
|
"보안",
|
|
76
76
|
"최적화",
|
|
77
|
-
// 한국어 — 디자인/문서 (
|
|
77
|
+
// 한국어 — 디자인/문서 (Antigravity 친화)
|
|
78
78
|
"디자인",
|
|
79
79
|
"문서화",
|
|
80
80
|
// 한국어 — 간단/빠른 (haiku 친화)
|