@estebanforge/pi-ask-antigravity 1.2.0 → 1.2.3
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/extensions/index.ts +210 -40
- package/package.json +14 -5
package/extensions/index.ts
CHANGED
|
@@ -83,13 +83,13 @@ type Mode = "plan" | "accept-edits";
|
|
|
83
83
|
// whose full string equals the overlay target, the live entry wins. When it
|
|
84
84
|
// does not (older agy, missing model, plan gate), the overlay entry resolves
|
|
85
85
|
// the alias so the user can still type "sonnet" and get a working answer.
|
|
86
|
-
// "sonnet" ->
|
|
87
|
-
// "opus" ->
|
|
88
|
-
// "gpt-oss" ->
|
|
86
|
+
// "sonnet" -> claude-sonnet-4-6
|
|
87
|
+
// "opus" -> claude-opus-4-6-thinking
|
|
88
|
+
// "gpt-oss" -> gpt-oss-120b-medium
|
|
89
89
|
const STATIC_ALIAS_OVERLAY: ReadonlyArray<ModelEntry> = [
|
|
90
|
-
{ full: "
|
|
91
|
-
{ full: "
|
|
92
|
-
{ full: "
|
|
90
|
+
{ full: "claude-sonnet-4-6", family: "other", version: null, tier: null },
|
|
91
|
+
{ full: "claude-opus-4-6-thinking", family: "other", version: null, tier: null },
|
|
92
|
+
{ full: "gpt-oss-120b-medium", family: "other", version: null, tier: null },
|
|
93
93
|
];
|
|
94
94
|
|
|
95
95
|
// Short alias → overlay full string. Used by resolveModel to recognize
|
|
@@ -97,15 +97,15 @@ const STATIC_ALIAS_OVERLAY: ReadonlyArray<ModelEntry> = [
|
|
|
97
97
|
// not match. The overlay entries are also merged into the live catalog for
|
|
98
98
|
// exact-string passthrough, so this map only needs to cover the short names.
|
|
99
99
|
const STATIC_SHORT_ALIAS: ReadonlyMap<string, string> = new Map([
|
|
100
|
-
["sonnet", "
|
|
101
|
-
["opus", "
|
|
102
|
-
["gpt-oss", "
|
|
100
|
+
["sonnet", "claude-sonnet-4-6"],
|
|
101
|
+
["opus", "claude-opus-4-6-thinking"],
|
|
102
|
+
["gpt-oss", "gpt-oss-120b-medium"],
|
|
103
103
|
]);
|
|
104
104
|
|
|
105
105
|
/** Merge the live catalog with the static alias overlay. Live entries win on
|
|
106
106
|
* case-insensitive full-string equality so an updated `agy models` listing
|
|
107
107
|
* always takes precedence over the hardcoded fallback. */
|
|
108
|
-
function mergeCatalog(live: ModelEntry[]): ModelEntry[] {
|
|
108
|
+
export function mergeCatalog(live: ModelEntry[]): ModelEntry[] {
|
|
109
109
|
const seen = new Set(live.map((e) => e.full.toLowerCase()));
|
|
110
110
|
const merged = [...live];
|
|
111
111
|
for (const entry of STATIC_ALIAS_OVERLAY) {
|
|
@@ -139,13 +139,22 @@ COMPACT OUTPUT (param: digest): when true, the prompt is prefixed to request com
|
|
|
139
139
|
type ThinkingTier = "low" | "medium" | "high";
|
|
140
140
|
type Family = "flash" | "pro" | "other";
|
|
141
141
|
|
|
142
|
-
interface ModelEntry {
|
|
143
|
-
full: string; // exact agy
|
|
142
|
+
export interface ModelEntry {
|
|
143
|
+
full: string; // exact agy slug, e.g. "gemini-3.6-flash-medium"
|
|
144
144
|
family: Family;
|
|
145
|
-
version: string | null; // "3.
|
|
145
|
+
version: string | null; // "3.6"
|
|
146
146
|
tier: ThinkingTier | null;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
/** Argv-facing model resolution: the exact --model slug plus an optional
|
|
150
|
+
* --effort tier. Gemini bases split the tier out (the base slug alone is
|
|
151
|
+
* invalid without --effort); fixed-thinking families keep agy's exact slug
|
|
152
|
+
* and carry no effort. */
|
|
153
|
+
export interface ResolvedModel {
|
|
154
|
+
model: string;
|
|
155
|
+
effort?: ThinkingTier;
|
|
156
|
+
}
|
|
157
|
+
|
|
149
158
|
interface Config {
|
|
150
159
|
defaultModel: string;
|
|
151
160
|
defaultThinking: ThinkingTier;
|
|
@@ -243,8 +252,11 @@ function saveConfig(patch: Partial<Config>): SaveResult {
|
|
|
243
252
|
// --- Model parsing + alias resolution --------------------------------------
|
|
244
253
|
|
|
245
254
|
/** Parse one `agy models` line into a structured entry. */
|
|
246
|
-
function parseModelLine(line: string): ModelEntry | null {
|
|
247
|
-
|
|
255
|
+
export function parseModelLine(line: string): ModelEntry | null {
|
|
256
|
+
// agy prints TWO columns: "<slug> <display label>". --model takes only the
|
|
257
|
+
// slug (col 1), so split it off; the label is display-only. A bare-slug line
|
|
258
|
+
// (no whitespace) splits to itself.
|
|
259
|
+
const full = line.trim().split(/\s+/)[0] ?? "";
|
|
248
260
|
if (!full) return null;
|
|
249
261
|
|
|
250
262
|
const lower = full.toLowerCase();
|
|
@@ -257,7 +269,7 @@ function parseModelLine(line: string): ModelEntry | null {
|
|
|
257
269
|
const versionMatch = lower.match(/(\d+\.\d+)/);
|
|
258
270
|
const version = versionMatch ? versionMatch[1] : null;
|
|
259
271
|
|
|
260
|
-
const tierMatch = lower.match(
|
|
272
|
+
const tierMatch = lower.match(/-(low|medium|high)$/);
|
|
261
273
|
const tier = tierMatch ? (tierMatch[1] as ThinkingTier) : null;
|
|
262
274
|
|
|
263
275
|
return { full, family, version, tier };
|
|
@@ -276,21 +288,34 @@ function nearestTier(available: ThinkingTier[], preferred: ThinkingTier): Thinki
|
|
|
276
288
|
return sorted[0] ?? preferred;
|
|
277
289
|
}
|
|
278
290
|
|
|
291
|
+
/** Build the argv-facing resolution from a picked catalog entry. Gemini bases
|
|
292
|
+
* (slugs starting "gemini-") accept a separate --effort, so split the tier
|
|
293
|
+
* suffix out of the slug: the base alone (gemini-3.6-flash) is what --model
|
|
294
|
+
* wants, and the tier goes to --effort. Fixed-thinking families keep agy's
|
|
295
|
+
* exact slug even when it carries a -medium suffix (gpt-oss-120b-medium):
|
|
296
|
+
* agy rejects --effort for them, so the suffix stays part of the slug. */
|
|
297
|
+
function toResolved(full: string, tier: ThinkingTier | null): ResolvedModel {
|
|
298
|
+
if (tier && full.toLowerCase().startsWith("gemini-")) {
|
|
299
|
+
return { model: full.replace(/-(low|medium|high)$/, ""), effort: tier };
|
|
300
|
+
}
|
|
301
|
+
return { model: full };
|
|
302
|
+
}
|
|
303
|
+
|
|
279
304
|
/**
|
|
280
|
-
* Resolve a friendly alias / partial name to an
|
|
281
|
-
* Returns null
|
|
282
|
-
*
|
|
305
|
+
* Resolve a friendly alias / partial name to an argv-facing {model, effort?}.
|
|
306
|
+
* Returns null only when the family is unrecognized; the caller then passes
|
|
307
|
+
* the raw input straight to agy.
|
|
283
308
|
*/
|
|
284
|
-
function resolveModel(
|
|
309
|
+
export function resolveModel(
|
|
285
310
|
input: string,
|
|
286
311
|
entries: ModelEntry[],
|
|
287
312
|
defaultThinking: ThinkingTier,
|
|
288
|
-
):
|
|
313
|
+
): ResolvedModel | null {
|
|
289
314
|
const lower = input.toLowerCase().trim();
|
|
290
315
|
|
|
291
316
|
// 1. Exact full-string match (case-insensitive).
|
|
292
317
|
const exact = entries.find((e) => e.full.toLowerCase() === lower);
|
|
293
|
-
if (exact) return exact.full;
|
|
318
|
+
if (exact) return toResolved(exact.full, exact.tier);
|
|
294
319
|
|
|
295
320
|
// 1b. Static short alias ("sonnet" / "opus" / "gpt-oss"). Checked
|
|
296
321
|
// before the family parser because none of these names contain
|
|
@@ -303,9 +328,8 @@ function resolveModel(
|
|
|
303
328
|
// the model under different casing than the overlay.
|
|
304
329
|
if (STATIC_SHORT_ALIAS.has(lower)) {
|
|
305
330
|
const target = STATIC_SHORT_ALIAS.get(lower) as string;
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
return fromCatalog ? fromCatalog.full : target;
|
|
331
|
+
const fromCatalog = entries.find((e) => e.full.toLowerCase() === target.toLowerCase());
|
|
332
|
+
return toResolved(fromCatalog?.full ?? target, fromCatalog?.tier ?? null);
|
|
309
333
|
}
|
|
310
334
|
|
|
311
335
|
// 2. Parse the alias.
|
|
@@ -365,13 +389,14 @@ function resolveModel(
|
|
|
365
389
|
const familyTiers = new Set(
|
|
366
390
|
candidates.map((e) => e.tier).filter((t): t is ThinkingTier => t !== null),
|
|
367
391
|
);
|
|
368
|
-
if (familyTiers.size === 0) return candidates[0].full; // no tiers on any entry
|
|
392
|
+
if (familyTiers.size === 0) return toResolved(candidates[0].full, null); // no tiers on any entry
|
|
369
393
|
|
|
370
394
|
const preferred =
|
|
371
395
|
tier ??
|
|
372
396
|
(familyTiers.has(defaultThinking) ? defaultThinking : FAMILY_DEFAULT_TIER[family]);
|
|
373
397
|
const chosenTier = nearestTier([...familyTiers], preferred);
|
|
374
|
-
|
|
398
|
+
const picked = candidates.find((e) => e.tier === chosenTier) ?? candidates[0];
|
|
399
|
+
return toResolved(picked.full, picked.tier);
|
|
375
400
|
}
|
|
376
401
|
|
|
377
402
|
// --- agy process helpers ---------------------------------------------------
|
|
@@ -500,13 +525,124 @@ function snapshotConversations(dir: string): Set<string> {
|
|
|
500
525
|
return out;
|
|
501
526
|
}
|
|
502
527
|
|
|
503
|
-
/**
|
|
504
|
-
*
|
|
505
|
-
|
|
528
|
+
/** Resolve which of the `candidates` DB ids is held open by the process tree
|
|
529
|
+
* rooted at `rootPid`. Used to disambiguate concurrent agy runs. Returns the
|
|
530
|
+
* single matching id, or null when none/several are open or /proc is
|
|
531
|
+
* unavailable. Ported from pi-antigravity-bridge src/discovery.ts; keep in
|
|
532
|
+
* sync. */
|
|
533
|
+
type OpenDbResolver = (
|
|
534
|
+
rootPid: number,
|
|
535
|
+
dir: string,
|
|
536
|
+
candidates: Set<string>,
|
|
537
|
+
) => string | null;
|
|
538
|
+
|
|
539
|
+
function readProcStat(pid: number): { pid: number; ppid: number } | null {
|
|
540
|
+
let raw: string;
|
|
541
|
+
try {
|
|
542
|
+
raw = fs.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
543
|
+
} catch {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
const closeParen = raw.lastIndexOf(")");
|
|
547
|
+
if (closeParen < 0) return null;
|
|
548
|
+
const fields = raw.slice(closeParen + 2).trim().split(/\s+/);
|
|
549
|
+
const ppid = Number(fields[1]);
|
|
550
|
+
if (!Number.isFinite(ppid)) return null;
|
|
551
|
+
return { pid, ppid };
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function collectDescendants(rootPid: number): Set<number> {
|
|
555
|
+
const out = new Set<number>([rootPid]);
|
|
556
|
+
let entries: string[];
|
|
557
|
+
try {
|
|
558
|
+
entries = fs.readdirSync("/proc");
|
|
559
|
+
} catch {
|
|
560
|
+
return out;
|
|
561
|
+
}
|
|
562
|
+
const ppidOf = new Map<number, number>();
|
|
563
|
+
for (const e of entries) {
|
|
564
|
+
if (!/^\d+$/.test(e)) continue;
|
|
565
|
+
const s = readProcStat(Number(e));
|
|
566
|
+
if (s) ppidOf.set(s.pid, s.ppid);
|
|
567
|
+
}
|
|
568
|
+
let changed = true;
|
|
569
|
+
while (changed) {
|
|
570
|
+
changed = false;
|
|
571
|
+
for (const [pid, ppid] of ppidOf) {
|
|
572
|
+
if (out.has(pid)) continue;
|
|
573
|
+
if (out.has(ppid)) {
|
|
574
|
+
out.add(pid);
|
|
575
|
+
changed = true;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return out;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function safeRealpath(p: string): string | null {
|
|
583
|
+
try {
|
|
584
|
+
return fs.realpathSync(p);
|
|
585
|
+
} catch {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const procTreeOpenDbResolver: OpenDbResolver = (rootPid, dir, candidates) => {
|
|
591
|
+
if (candidates.size <= 1) return null;
|
|
592
|
+
if (process.platform !== "linux") return null;
|
|
593
|
+
const dirResolved = safeRealpath(dir);
|
|
594
|
+
const tree = collectDescendants(rootPid);
|
|
595
|
+
const found = new Set<string>();
|
|
596
|
+
for (const pid of tree) {
|
|
597
|
+
let fds: string[];
|
|
598
|
+
try {
|
|
599
|
+
fds = fs.readdirSync(`/proc/${pid}/fd`);
|
|
600
|
+
} catch {
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
for (const fd of fds) {
|
|
604
|
+
let target: string;
|
|
605
|
+
try {
|
|
606
|
+
target = fs.readlinkSync(`/proc/${pid}/fd/${fd}`);
|
|
607
|
+
} catch {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
const base = path.basename(target);
|
|
611
|
+
if (!base.endsWith(".db")) continue;
|
|
612
|
+
if (dirResolved && safeRealpath(path.dirname(target)) !== dirResolved) continue;
|
|
613
|
+
const id = base.slice(0, -3);
|
|
614
|
+
if (candidates.has(id)) found.add(id);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (found.size === 1) return [...found][0] ?? null;
|
|
618
|
+
return null;
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
interface BindOptions {
|
|
622
|
+
pid?: number;
|
|
623
|
+
resolveOpenDb?: OpenDbResolver;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** Find the conversation id created since `before`. Returns null when none
|
|
627
|
+
* appeared, or when several appeared and we cannot tie one to our process.
|
|
628
|
+
* Pass `opts.pid` (the spawned agy) to enable concurrent-run disambiguation
|
|
629
|
+
* via the process-tree FD scan. */
|
|
630
|
+
function newConversationId(
|
|
631
|
+
dir: string,
|
|
632
|
+
before: Set<string>,
|
|
633
|
+
opts: BindOptions = {},
|
|
634
|
+
): string | null {
|
|
506
635
|
const created = [...snapshotConversations(dir)].filter((id) => !before.has(id));
|
|
507
636
|
if (created.length === 0) return null;
|
|
508
|
-
if (created.length
|
|
509
|
-
|
|
637
|
+
if (created.length === 1) return created[0] ?? null;
|
|
638
|
+
// Ambiguous: try to authoritatively identify ours via the spawned
|
|
639
|
+
// process's open files. Fail safe to null when we can't pick exactly one.
|
|
640
|
+
if (opts.pid !== undefined) {
|
|
641
|
+
const resolve = opts.resolveOpenDb ?? procTreeOpenDbResolver;
|
|
642
|
+
const hit = resolve(opts.pid, dir, new Set(created));
|
|
643
|
+
if (hit && created.includes(hit)) return hit;
|
|
644
|
+
}
|
|
645
|
+
return null;
|
|
510
646
|
}
|
|
511
647
|
|
|
512
648
|
// --- Extension -------------------------------------------------------------
|
|
@@ -560,7 +696,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
560
696
|
` defaultModel: ${config.defaultModel}`,
|
|
561
697
|
` defaultThinking: ${config.defaultThinking}`,
|
|
562
698
|
` permissions: ${config.skipPermissions ? "auto-approved" : "prompt"}`,
|
|
563
|
-
` resolved: ${resolveModel(config.defaultModel, discovered, config.defaultThinking) ?? "(agy default)"}`,
|
|
699
|
+
` resolved: ${resolveModel(config.defaultModel, discovered, config.defaultThinking)?.model ?? "(agy default)"}`,
|
|
564
700
|
``,
|
|
565
701
|
`Edit: ~/.pi/agent/ask-antigravity.json`,
|
|
566
702
|
].join("\n"),
|
|
@@ -571,7 +707,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
571
707
|
|
|
572
708
|
// Resolve the display string for the current default model.
|
|
573
709
|
const currentResolved =
|
|
574
|
-
resolveModel(config.defaultModel, discovered, config.defaultThinking) ?? config.defaultModel;
|
|
710
|
+
resolveModel(config.defaultModel, discovered, config.defaultThinking)?.model ?? config.defaultModel;
|
|
575
711
|
|
|
576
712
|
const items: SettingItem[] = [
|
|
577
713
|
{
|
|
@@ -581,7 +717,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
581
717
|
"Friendly alias resolved to the latest matching agy model. 'flash' = latest Flash, 'pro' = latest Pro, 'gemini' = latest Flash.",
|
|
582
718
|
currentValue: `${config.defaultModel} → ${currentResolved}`,
|
|
583
719
|
values: MODEL_OPTIONS.map((m) => {
|
|
584
|
-
const r = resolveModel(m, discovered, config.defaultThinking) ?? m;
|
|
720
|
+
const r = resolveModel(m, discovered, config.defaultThinking)?.model ?? m;
|
|
585
721
|
return `${m} → ${r}`;
|
|
586
722
|
}),
|
|
587
723
|
},
|
|
@@ -766,7 +902,9 @@ export default async function (pi: ExtensionAPI) {
|
|
|
766
902
|
};
|
|
767
903
|
}
|
|
768
904
|
const resolved =
|
|
769
|
-
resolveModel(requestedModel, discovered, config.defaultThinking) ??
|
|
905
|
+
resolveModel(requestedModel, discovered, config.defaultThinking) ?? {
|
|
906
|
+
model: requestedModel,
|
|
907
|
+
};
|
|
770
908
|
|
|
771
909
|
const start = Date.now();
|
|
772
910
|
const cwd = params.cwd || ctx.cwd || process.cwd();
|
|
@@ -777,13 +915,13 @@ export default async function (pi: ExtensionAPI) {
|
|
|
777
915
|
if (!stat.isDirectory()) {
|
|
778
916
|
return {
|
|
779
917
|
content: [{ type: "text", text: `cwd is not a directory: ${cwd}` }],
|
|
780
|
-
details: emptyDetails(requestedModel, resolved),
|
|
918
|
+
details: emptyDetails(requestedModel, resolved.model),
|
|
781
919
|
};
|
|
782
920
|
}
|
|
783
921
|
} catch {
|
|
784
922
|
return {
|
|
785
923
|
content: [{ type: "text", text: `cwd does not exist: ${cwd}` }],
|
|
786
|
-
details: emptyDetails(requestedModel, resolved),
|
|
924
|
+
details: emptyDetails(requestedModel, resolved.model),
|
|
787
925
|
};
|
|
788
926
|
}
|
|
789
927
|
|
|
@@ -816,7 +954,8 @@ export default async function (pi: ExtensionAPI) {
|
|
|
816
954
|
const args: string[] = ["--add-dir", cwd];
|
|
817
955
|
const extra = extraArgs();
|
|
818
956
|
if (extra.length) args.push(...extra);
|
|
819
|
-
if (resolved) args.push("--model", resolved);
|
|
957
|
+
if (resolved.model) args.push("--model", resolved.model);
|
|
958
|
+
if (resolved.effort) args.push("--effort", resolved.effort);
|
|
820
959
|
args.push("--mode", mode);
|
|
821
960
|
// accept-edits auto-approves file edits but NOT shell commands, so a
|
|
822
961
|
// run_command would hang on an unanswerable y/n prompt in non-interactive
|
|
@@ -828,7 +967,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
828
967
|
|
|
829
968
|
const details: AgyDetails = {
|
|
830
969
|
model: requestedModel,
|
|
831
|
-
resolvedModel: resolved,
|
|
970
|
+
resolvedModel: resolved.model,
|
|
832
971
|
mode,
|
|
833
972
|
digest: useDigest,
|
|
834
973
|
conversationId: isContinuation ? (rawConvId as string) : null,
|
|
@@ -859,6 +998,11 @@ export default async function (pi: ExtensionAPI) {
|
|
|
859
998
|
: null;
|
|
860
999
|
|
|
861
1000
|
try {
|
|
1001
|
+
// Bind the conversation id DURING the run (agy is alive then) so the
|
|
1002
|
+
// pid-based /proc FD resolver can disambiguate when a concurrent agy
|
|
1003
|
+
// also drops a new .db. Awaited after the run; the post-exit loop
|
|
1004
|
+
// below is the fallback for runs that exit before the poll binds.
|
|
1005
|
+
let bindDuringRun: Promise<void> = Promise.resolve();
|
|
862
1006
|
const outcome = await new Promise<{
|
|
863
1007
|
exitCode: number;
|
|
864
1008
|
aborted: boolean;
|
|
@@ -886,6 +1030,28 @@ export default async function (pi: ExtensionAPI) {
|
|
|
886
1030
|
details.stderr += d;
|
|
887
1031
|
});
|
|
888
1032
|
|
|
1033
|
+
// Concurrent bind: poll for the new id while agy is alive. The FD
|
|
1034
|
+
// resolver needs a live process tree, so this stops (and the
|
|
1035
|
+
// post-exit fallback below takes over) once agy has exited.
|
|
1036
|
+
if (!isContinuation && snapshot && proc.pid) {
|
|
1037
|
+
bindDuringRun = (async () => {
|
|
1038
|
+
for (let attempt = 0; attempt < DISCOVERY_POLL_ATTEMPTS; attempt++) {
|
|
1039
|
+
if (details.conversationId) return;
|
|
1040
|
+
if (proc.exitCode !== null) return; // agy gone: scan useless now
|
|
1041
|
+
const found = newConversationId(CONVERSATIONS_DIR, snapshot, {
|
|
1042
|
+
pid: proc.pid,
|
|
1043
|
+
});
|
|
1044
|
+
if (found) {
|
|
1045
|
+
details.conversationId = found;
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
await sleep(DISCOVERY_POLL_MS);
|
|
1049
|
+
}
|
|
1050
|
+
})().catch(() => {
|
|
1051
|
+
/* best-effort: a bind error must never fail an otherwise-OK turn */
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
|
|
889
1055
|
let sigkillTimer: ReturnType<typeof setTimeout> | undefined;
|
|
890
1056
|
let watchdog: ReturnType<typeof setTimeout> | undefined;
|
|
891
1057
|
let settled = false;
|
|
@@ -952,6 +1118,10 @@ export default async function (pi: ExtensionAPI) {
|
|
|
952
1118
|
|
|
953
1119
|
if (statusInterval) clearInterval(statusInterval);
|
|
954
1120
|
|
|
1121
|
+
// Let the during-run bind poll finish (it bails immediately once agy
|
|
1122
|
+
// has exited, so this rarely blocks).
|
|
1123
|
+
await bindDuringRun;
|
|
1124
|
+
|
|
955
1125
|
details.exitCode = outcome.exitCode;
|
|
956
1126
|
details.aborted = outcome.aborted;
|
|
957
1127
|
details.timedOut = outcome.timedOut;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@estebanforge/pi-ask-antigravity",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Pi tool that delegates a self-contained sub-task to Google Antigravity's agy CLI (the CLI for Gemini). Friendly model aliases (flash/pro/gemini), configurable default model + thinking, and the AskAntigravity delegation tool.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -22,7 +22,15 @@
|
|
|
22
22
|
"type": "git",
|
|
23
23
|
"url": "git+https://github.com/EstebanForge/pi-ask-antigravity.git"
|
|
24
24
|
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
25
28
|
"type": "module",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"prepublishOnly": "npm run typecheck && npm test"
|
|
33
|
+
},
|
|
26
34
|
"files": [
|
|
27
35
|
"extensions",
|
|
28
36
|
"README.md"
|
|
@@ -41,11 +49,12 @@
|
|
|
41
49
|
}
|
|
42
50
|
},
|
|
43
51
|
"devDependencies": {
|
|
44
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
45
|
-
"@earendil-works/pi-ai": "^0.
|
|
46
|
-
"@earendil-works/pi-tui": "^0.
|
|
52
|
+
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
53
|
+
"@earendil-works/pi-ai": "^0.84.0",
|
|
54
|
+
"@earendil-works/pi-tui": "^0.84.0",
|
|
47
55
|
"@types/node": "^22.0.0",
|
|
48
56
|
"typebox": "^1.1.38",
|
|
49
|
-
"typescript": "^5.8.0"
|
|
57
|
+
"typescript": "^5.8.0",
|
|
58
|
+
"vitest": "^4.1.7"
|
|
50
59
|
}
|
|
51
60
|
}
|