@mrclrchtr/supi-antigravity 6.4.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/CLAUDE.md +21 -0
- package/CONTEXT.md +53 -0
- package/README.md +75 -0
- package/docs/adr/0001-use-an-isolated-antigravity-home.md +5 -0
- package/node_modules/@mrclrchtr/supi-core/README.md +118 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +76 -0
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +40 -0
- package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +232 -0
- package/node_modules/@mrclrchtr/supi-core/src/config/prompt-surface.ts +363 -0
- package/node_modules/@mrclrchtr/supi-core/src/config.ts +12 -0
- package/node_modules/@mrclrchtr/supi-core/src/context/context-provider-registry.ts +36 -0
- package/node_modules/@mrclrchtr/supi-core/src/context/context-tag.ts +31 -0
- package/node_modules/@mrclrchtr/supi-core/src/context.ts +8 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +308 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +120 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug.ts +14 -0
- package/node_modules/@mrclrchtr/supi-core/src/evidence-badge.ts +41 -0
- package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +57 -0
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +34 -0
- package/node_modules/@mrclrchtr/supi-core/src/llm.ts +201 -0
- package/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
- package/node_modules/@mrclrchtr/supi-core/src/path-utils.ts +44 -0
- package/node_modules/@mrclrchtr/supi-core/src/path.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/project-roots.ts +170 -0
- package/node_modules/@mrclrchtr/supi-core/src/project.ts +15 -0
- package/node_modules/@mrclrchtr/supi-core/src/prompt-surface.ts +4 -0
- package/node_modules/@mrclrchtr/supi-core/src/registry-utils.ts +93 -0
- package/node_modules/@mrclrchtr/supi-core/src/report.ts +121 -0
- package/node_modules/@mrclrchtr/supi-core/src/session-utils.ts +71 -0
- package/node_modules/@mrclrchtr/supi-core/src/session.ts +8 -0
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +105 -0
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-schema.ts +453 -0
- package/node_modules/@mrclrchtr/supi-core/src/settings.ts +36 -0
- package/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
- package/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
- package/node_modules/@mrclrchtr/supi-core/src/terminal.ts +60 -0
- package/package.json +79 -0
- package/scripts/live-probe.ts +161 -0
- package/src/activity.ts +22 -0
- package/src/availability.ts +231 -0
- package/src/catalogue.ts +21 -0
- package/src/config.ts +26 -0
- package/src/conversation/handles.ts +183 -0
- package/src/extension.ts +22 -0
- package/src/isolated-home.ts +346 -0
- package/src/process/environment.ts +33 -0
- package/src/process/event-values.ts +279 -0
- package/src/process/events.ts +365 -0
- package/src/process/hooks.ts +219 -0
- package/src/process/ndjson.ts +120 -0
- package/src/process/protocol.ts +69 -0
- package/src/process/runner.ts +147 -0
- package/src/process/subprocess.ts +278 -0
- package/src/process/usage.ts +50 -0
- package/src/runtime.ts +118 -0
- package/src/settings.ts +38 -0
- package/src/structured-output.ts +58 -0
- package/src/tool/antigravity_run/evidence.ts +169 -0
- package/src/tool/antigravity_run/execute.ts +225 -0
- package/src/tool/antigravity_run/guidance.ts +3 -0
- package/src/tool/antigravity_run/input.ts +106 -0
- package/src/tool/antigravity_run/register.ts +36 -0
- package/src/tool/antigravity_run/render.ts +236 -0
- package/src/tool/antigravity_run/result.ts +186 -0
- package/src/tool/antigravity_run/spec.ts +20 -0
- package/src/types.ts +107 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/** Compact TUI renderers for Antigravity Run calls and results. */
|
|
2
|
+
import { getMarkdownTheme, type Theme } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
|
|
4
|
+
import type {
|
|
5
|
+
AntigravityRunResultDetails,
|
|
6
|
+
ClassifiedSource,
|
|
7
|
+
ClassifiedWorkspaceEvidence,
|
|
8
|
+
} from "../../types.ts";
|
|
9
|
+
import { ANTIGRAVITY_RUN_TOOL_LABEL } from "./spec.ts";
|
|
10
|
+
|
|
11
|
+
const MAX_PROMPT_PREVIEW = 120;
|
|
12
|
+
const MAX_ERROR_PREVIEW = 500;
|
|
13
|
+
|
|
14
|
+
/** Render the tool name, safe prompt preview, model, and workspace status. */
|
|
15
|
+
export function renderAntigravityCall(args: unknown, theme: Theme, _context?: unknown): Text {
|
|
16
|
+
const params = isRecord(args) ? args : {};
|
|
17
|
+
const prompt = typeof params.prompt === "string" ? safePreview(params.prompt) : undefined;
|
|
18
|
+
const newInput = isRecord(params.new) ? params.new : undefined;
|
|
19
|
+
const model = typeof newInput?.model === "string" ? newInput.model : undefined;
|
|
20
|
+
const workspace = typeof newInput?.workspace === "boolean" ? newInput.workspace : undefined;
|
|
21
|
+
const continuation = isRecord(params.continue);
|
|
22
|
+
const parts = [
|
|
23
|
+
theme.fg("toolTitle", ANTIGRAVITY_RUN_TOOL_LABEL),
|
|
24
|
+
model ? theme.fg("accent", model) : continuation ? theme.fg("accent", "follow-up") : undefined,
|
|
25
|
+
workspace === undefined
|
|
26
|
+
? undefined
|
|
27
|
+
: theme.fg("dim", workspace ? "workspace" : "Consultation Workspace"),
|
|
28
|
+
prompt ? theme.fg("dim", `· ${prompt}`) : undefined,
|
|
29
|
+
].filter((part): part is string => part !== undefined);
|
|
30
|
+
return new Text(parts.join(" "), 0, 0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Render partial, collapsed, expanded, and error Antigravity states. */
|
|
34
|
+
export function renderAntigravityResult(
|
|
35
|
+
result: {
|
|
36
|
+
content?: Array<{ type: string; text?: string }>;
|
|
37
|
+
details?: unknown;
|
|
38
|
+
isError?: boolean;
|
|
39
|
+
},
|
|
40
|
+
options: { expanded: boolean; isPartial: boolean },
|
|
41
|
+
theme: Theme,
|
|
42
|
+
context: { isError?: boolean } = {},
|
|
43
|
+
): Container | Text {
|
|
44
|
+
if (options.isPartial) return renderPartial(result.details, options.expanded, theme);
|
|
45
|
+
if (result.isError || context.isError)
|
|
46
|
+
return renderError(result.content, options.expanded, theme);
|
|
47
|
+
const details = asDetails(result.details);
|
|
48
|
+
if (!details) return new Text(theme.fg("dim", "Antigravity Run finished."), 0, 0);
|
|
49
|
+
return options.expanded
|
|
50
|
+
? renderExpanded(details, result.content, theme)
|
|
51
|
+
: renderCollapsed(details, theme);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function renderPartial(detailsValue: unknown, expanded: boolean, theme: Theme): Text | Container {
|
|
55
|
+
const details = isRecord(detailsValue) ? detailsValue : {};
|
|
56
|
+
const model = typeof details.model === "string" ? details.model : "model pending";
|
|
57
|
+
const workspace = details.workspaceAccess === true ? "workspace" : "Consultation Workspace";
|
|
58
|
+
const activity =
|
|
59
|
+
typeof details.latestActivity === "string" ? safePreview(details.latestActivity) : "preparing";
|
|
60
|
+
const line = theme.fg("warning", `● ${ANTIGRAVITY_RUN_TOOL_LABEL} · ${model} · ${workspace}`);
|
|
61
|
+
if (!expanded) return new Text(`${line}\n${theme.fg("dim", activity)}`, 0, 0);
|
|
62
|
+
const container = new Container();
|
|
63
|
+
container.addChild(new Text(line, 0, 0));
|
|
64
|
+
container.addChild(new Text(theme.fg("dim", `activity: ${activity}`), 1, 0));
|
|
65
|
+
return container;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function renderCollapsed(details: AntigravityRunResultDetails, theme: Theme): Text {
|
|
69
|
+
const usage = formatUsage(details.usage);
|
|
70
|
+
const evidence = [
|
|
71
|
+
`${details.observedSources.length} observed source${details.observedSources.length === 1 ? "" : "s"}`,
|
|
72
|
+
`${details.observedWorkspaceEvidence.length} observed path${details.observedWorkspaceEvidence.length === 1 ? "" : "s"}`,
|
|
73
|
+
];
|
|
74
|
+
if (usage) evidence.push(usage);
|
|
75
|
+
const warning = details.warnings.length > 0 ? ` · ${details.warnings.length} warning(s)` : "";
|
|
76
|
+
return new Text(
|
|
77
|
+
`${theme.fg("success", "✓ Antigravity Run complete")} · ${details.handle} · ${evidence.join(" · ")}${warning}`,
|
|
78
|
+
0,
|
|
79
|
+
0,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function renderExpanded(
|
|
84
|
+
details: AntigravityRunResultDetails,
|
|
85
|
+
content: Array<{ type: string; text?: string }> | undefined,
|
|
86
|
+
theme: Theme,
|
|
87
|
+
): Container {
|
|
88
|
+
const container = new Container();
|
|
89
|
+
container.addChild(
|
|
90
|
+
new Text(
|
|
91
|
+
theme.fg(
|
|
92
|
+
"accent",
|
|
93
|
+
`${theme.bold("Antigravity Run complete")} · ${details.model} · ${details.workingDirectoryKind}`,
|
|
94
|
+
),
|
|
95
|
+
0,
|
|
96
|
+
0,
|
|
97
|
+
),
|
|
98
|
+
);
|
|
99
|
+
container.addChild(new Text(theme.fg("dim", `handle: ${details.handle}`), 1, 0));
|
|
100
|
+
container.addChild(
|
|
101
|
+
new Text(
|
|
102
|
+
theme.fg(
|
|
103
|
+
"dim",
|
|
104
|
+
`CLI: ${details.cliVersion} · ${formatUsage(details.usage) || "usage unavailable"}`,
|
|
105
|
+
),
|
|
106
|
+
1,
|
|
107
|
+
0,
|
|
108
|
+
),
|
|
109
|
+
);
|
|
110
|
+
container.addChild(
|
|
111
|
+
new Text(
|
|
112
|
+
theme.fg(
|
|
113
|
+
"dim",
|
|
114
|
+
`web: ${details.webUsed ? "used" : "not used"} · workspace: ${details.workspaceUsed ? "used" : "not used"} · project guidance: ${details.ambientProjectGuidanceAvailable ? "available" : "not available"}`,
|
|
115
|
+
),
|
|
116
|
+
1,
|
|
117
|
+
0,
|
|
118
|
+
),
|
|
119
|
+
);
|
|
120
|
+
renderSources(container, "Observed sources", details.observedSources, theme);
|
|
121
|
+
renderSources(container, "Claimed sources", details.claimedSources, theme);
|
|
122
|
+
renderWorkspace(container, "Observed workspace paths", details.observedWorkspaceEvidence, theme);
|
|
123
|
+
renderWorkspace(container, "Claimed workspace paths", details.claimedWorkspaceEvidence, theme);
|
|
124
|
+
if (details.warnings.length > 0) {
|
|
125
|
+
container.addChild(new Spacer(1));
|
|
126
|
+
container.addChild(new Text(theme.fg("warning", "Warnings"), 1, 0));
|
|
127
|
+
for (const warning of details.warnings)
|
|
128
|
+
container.addChild(new Text(theme.fg("dim", `- ${warning}`), 1, 0));
|
|
129
|
+
}
|
|
130
|
+
const markdown = content?.find((item) => item.type === "text")?.text;
|
|
131
|
+
if (markdown) {
|
|
132
|
+
container.addChild(new Spacer(1));
|
|
133
|
+
container.addChild(new Markdown(markdown, 1, 0, getMarkdownTheme()));
|
|
134
|
+
}
|
|
135
|
+
return container;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function renderSources(
|
|
139
|
+
container: Container,
|
|
140
|
+
title: string,
|
|
141
|
+
sources: readonly ClassifiedSource[],
|
|
142
|
+
theme: Theme,
|
|
143
|
+
): void {
|
|
144
|
+
if (sources.length === 0) return;
|
|
145
|
+
container.addChild(new Spacer(1));
|
|
146
|
+
container.addChild(new Text(theme.fg("muted", title), 1, 0));
|
|
147
|
+
for (const source of sources) {
|
|
148
|
+
container.addChild(
|
|
149
|
+
new Text(theme.fg("dim", `- ${safePreview(source.title)}: ${source.url}`), 2, 0),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function renderWorkspace(
|
|
155
|
+
container: Container,
|
|
156
|
+
title: string,
|
|
157
|
+
evidence: readonly ClassifiedWorkspaceEvidence[],
|
|
158
|
+
theme: Theme,
|
|
159
|
+
): void {
|
|
160
|
+
if (evidence.length === 0) return;
|
|
161
|
+
container.addChild(new Spacer(1));
|
|
162
|
+
container.addChild(new Text(theme.fg("muted", title), 1, 0));
|
|
163
|
+
for (const item of evidence) {
|
|
164
|
+
container.addChild(
|
|
165
|
+
new Text(theme.fg("dim", `- ${item.path}: ${safePreview(item.summary)}`), 2, 0),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function renderError(
|
|
171
|
+
content: Array<{ type: string; text?: string }> | undefined,
|
|
172
|
+
expanded: boolean,
|
|
173
|
+
theme: Theme,
|
|
174
|
+
): Text {
|
|
175
|
+
const text = content?.find((item) => item.type === "text")?.text;
|
|
176
|
+
const message = text ? safePreview(text, expanded ? MAX_ERROR_PREVIEW : 160) : "operation failed";
|
|
177
|
+
return new Text(theme.fg("error", `Antigravity Run failed: ${message}`), 0, 0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function asDetails(value: unknown): AntigravityRunResultDetails | undefined {
|
|
181
|
+
if (!isRecord(value)) return undefined;
|
|
182
|
+
if (
|
|
183
|
+
typeof value.model !== "string" ||
|
|
184
|
+
typeof value.handle !== "string" ||
|
|
185
|
+
typeof value.cliVersion !== "string" ||
|
|
186
|
+
(value.workingDirectoryKind !== "workspace" && value.workingDirectoryKind !== "consultation") ||
|
|
187
|
+
!Array.isArray(value.observedSources) ||
|
|
188
|
+
!Array.isArray(value.claimedSources) ||
|
|
189
|
+
!Array.isArray(value.observedWorkspaceEvidence) ||
|
|
190
|
+
!Array.isArray(value.claimedWorkspaceEvidence) ||
|
|
191
|
+
!Array.isArray(value.warnings) ||
|
|
192
|
+
!value.observedSources.every(isClassifiedSource) ||
|
|
193
|
+
!value.claimedSources.every(isClassifiedSource) ||
|
|
194
|
+
!value.observedWorkspaceEvidence.every(isClassifiedWorkspaceEvidence) ||
|
|
195
|
+
!value.claimedWorkspaceEvidence.every(isClassifiedWorkspaceEvidence) ||
|
|
196
|
+
!value.warnings.every((warning) => typeof warning === "string")
|
|
197
|
+
) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
return value as unknown as AntigravityRunResultDetails;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function formatUsage(
|
|
204
|
+
usage: { totalTokens?: number; inputTokens?: number; outputTokens?: number } | undefined,
|
|
205
|
+
): string {
|
|
206
|
+
if (!usage) return "";
|
|
207
|
+
const total = usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
|
|
208
|
+
return `${total.toLocaleString("en-US")} tokens`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function isClassifiedSource(value: unknown): value is ClassifiedSource {
|
|
212
|
+
return (
|
|
213
|
+
isRecord(value) &&
|
|
214
|
+
typeof value.title === "string" &&
|
|
215
|
+
typeof value.url === "string" &&
|
|
216
|
+
(value.evidence === "observed" || value.evidence === "claimed")
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function isClassifiedWorkspaceEvidence(value: unknown): value is ClassifiedWorkspaceEvidence {
|
|
221
|
+
return (
|
|
222
|
+
isRecord(value) &&
|
|
223
|
+
typeof value.path === "string" &&
|
|
224
|
+
typeof value.summary === "string" &&
|
|
225
|
+
(value.evidence === "observed" || value.evidence === "claimed")
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function safePreview(value: string, maxLength = MAX_PROMPT_PREVIEW): string {
|
|
230
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
231
|
+
return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 1)}…`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
235
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
236
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { Usage } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { isWebToolName, isWorkspaceToolName } from "../../activity.ts";
|
|
4
|
+
import type {
|
|
5
|
+
AntigravityExecutionFacts,
|
|
6
|
+
AntigravityRunResultDetails,
|
|
7
|
+
AntigravityUsage,
|
|
8
|
+
ClassifiedSource,
|
|
9
|
+
ClassifiedWorkspaceEvidence,
|
|
10
|
+
ConversationHandleRecord,
|
|
11
|
+
} from "../../types.ts";
|
|
12
|
+
import { classifyAnswerEvidence } from "./evidence.ts";
|
|
13
|
+
|
|
14
|
+
/** PI's hard result bounds for model-visible Antigravity output. */
|
|
15
|
+
export const MAX_RESULT_BYTES = 50 * 1024;
|
|
16
|
+
export const MAX_RESULT_LINES = 2_000;
|
|
17
|
+
|
|
18
|
+
/** Inputs needed to assemble one completed Antigravity Run result. */
|
|
19
|
+
export interface AntigravityResultOptions {
|
|
20
|
+
facts: AntigravityExecutionFacts;
|
|
21
|
+
handle: ConversationHandleRecord;
|
|
22
|
+
durationMs: number;
|
|
23
|
+
hookWarning?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Assemble model-visible evidence and free structured renderer details. */
|
|
27
|
+
export function buildAntigravityResult(
|
|
28
|
+
options: AntigravityResultOptions,
|
|
29
|
+
): AgentToolResult<AntigravityRunResultDetails> {
|
|
30
|
+
const { facts, handle } = options;
|
|
31
|
+
const evidence = classifyAnswerEvidence(facts.answer, facts, handle.canonicalWorkingDirectory);
|
|
32
|
+
const webUsed = facts.successfulToolNames.some(isWebToolName);
|
|
33
|
+
const workspaceUsed = facts.successfulToolNames.some(isWorkspaceToolName);
|
|
34
|
+
const warnings = buildWarnings({
|
|
35
|
+
evidenceWarnings: evidence.warnings,
|
|
36
|
+
observedSourceCount: evidence.observedSources.length,
|
|
37
|
+
observedWorkspaceEvidenceCount: evidence.observedWorkspaceEvidence.length,
|
|
38
|
+
webUsed,
|
|
39
|
+
workspaceUsed,
|
|
40
|
+
permissionDenials: facts.permissionDenials,
|
|
41
|
+
hookWarning: options.hookWarning,
|
|
42
|
+
});
|
|
43
|
+
const details: AntigravityRunResultDetails = {
|
|
44
|
+
model: handle.model,
|
|
45
|
+
workingDirectoryKind: handle.workspaceAccess ? "workspace" : "consultation",
|
|
46
|
+
canonicalWorkingDirectory: handle.canonicalWorkingDirectory,
|
|
47
|
+
workspaceAccess: handle.workspaceAccess,
|
|
48
|
+
cliVersion: handle.cliVersion,
|
|
49
|
+
durationMs: Math.max(0, Math.floor(options.durationMs)),
|
|
50
|
+
...(facts.usage ? { usage: facts.usage } : {}),
|
|
51
|
+
observedToolNames: facts.observedToolNames,
|
|
52
|
+
observedToolCounts: facts.observedToolCounts,
|
|
53
|
+
permissionDenials: facts.permissionDenials,
|
|
54
|
+
webUsed,
|
|
55
|
+
workspaceUsed,
|
|
56
|
+
ambientProjectGuidanceAvailable: handle.workspaceAccess,
|
|
57
|
+
...(options.hookWarning ? { activeProjectHookWarning: options.hookWarning } : {}),
|
|
58
|
+
handle: handle.handle,
|
|
59
|
+
rawAntigravityId: handle.rawAntigravityId,
|
|
60
|
+
handleState: "active",
|
|
61
|
+
observedSources: evidence.observedSources,
|
|
62
|
+
claimedSources: evidence.claimedSources,
|
|
63
|
+
observedWorkspaceEvidence: evidence.observedWorkspaceEvidence,
|
|
64
|
+
claimedWorkspaceEvidence: evidence.claimedWorkspaceEvidence,
|
|
65
|
+
warnings,
|
|
66
|
+
};
|
|
67
|
+
const text = formatModelResult(facts.answer.answer, details);
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text }],
|
|
70
|
+
details,
|
|
71
|
+
...(facts.usage ? { usage: toPiUsage(facts.usage) } : {}),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function formatModelResult(answer: string, details: AntigravityRunResultDetails): string {
|
|
76
|
+
const sections = [
|
|
77
|
+
answer.trim(),
|
|
78
|
+
"",
|
|
79
|
+
`Web Capability: ${details.webUsed ? "used" : "not used"}`,
|
|
80
|
+
`Workspace Access: ${details.workspaceAccess ? "current workspace" : "Consultation Workspace"}`,
|
|
81
|
+
`Ambient Project Guidance: ${details.ambientProjectGuidanceAvailable ? "available" : "not available"}`,
|
|
82
|
+
formatSources("Observed Sources", details.observedSources),
|
|
83
|
+
formatSources("Claimed Sources (not observed)", details.claimedSources),
|
|
84
|
+
formatWorkspace("Observed Workspace Evidence", details.observedWorkspaceEvidence),
|
|
85
|
+
formatWorkspace("Claimed Workspace Evidence (not observed)", details.claimedWorkspaceEvidence),
|
|
86
|
+
formatWarnings(details.warnings),
|
|
87
|
+
`Conversation Handle: ${details.handle}`,
|
|
88
|
+
];
|
|
89
|
+
return boundResult(sections.filter((section) => section.length > 0).join("\n"));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function formatSources(title: string, sources: readonly ClassifiedSource[]): string {
|
|
93
|
+
if (sources.length === 0) return `${title}: none`;
|
|
94
|
+
return [
|
|
95
|
+
title,
|
|
96
|
+
...sources.map((source) => `- [${safeMarkdown(source.title)}](${source.url})`),
|
|
97
|
+
].join("\n");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function formatWorkspace(title: string, evidence: readonly ClassifiedWorkspaceEvidence[]): string {
|
|
101
|
+
if (evidence.length === 0) return `${title}: none`;
|
|
102
|
+
return [title, ...evidence.map((item) => `- ${item.path}: ${safeMarkdown(item.summary)}`)].join(
|
|
103
|
+
"\n",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function formatWarnings(warnings: readonly string[]): string {
|
|
108
|
+
return warnings.length > 0
|
|
109
|
+
? ["Warnings", ...warnings.map((warning) => `- ${warning}`)].join("\n")
|
|
110
|
+
: "Warnings: none";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function buildWarnings(options: {
|
|
114
|
+
evidenceWarnings: readonly string[];
|
|
115
|
+
observedSourceCount: number;
|
|
116
|
+
observedWorkspaceEvidenceCount: number;
|
|
117
|
+
webUsed: boolean;
|
|
118
|
+
workspaceUsed: boolean;
|
|
119
|
+
permissionDenials: number;
|
|
120
|
+
hookWarning?: string;
|
|
121
|
+
}): string[] {
|
|
122
|
+
const warnings = [...options.evidenceWarnings];
|
|
123
|
+
if (options.hookWarning) warnings.push(options.hookWarning);
|
|
124
|
+
if (options.permissionDenials > 0) {
|
|
125
|
+
warnings.push(
|
|
126
|
+
`${options.permissionDenials} Antigravity tool action(s) were denied by permissions.`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (options.webUsed && options.observedSourceCount === 0) {
|
|
130
|
+
warnings.push("Antigravity used web tools, but no source URL was observed in the answer.");
|
|
131
|
+
}
|
|
132
|
+
if (options.workspaceUsed && options.observedWorkspaceEvidenceCount === 0) {
|
|
133
|
+
warnings.push(
|
|
134
|
+
"Antigravity used workspace tools, but no workspace path was observed in the answer.",
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return [...new Set(warnings)].slice(0, 24);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function boundResult(value: string): string {
|
|
141
|
+
const lines = value.split(/\r?\n/);
|
|
142
|
+
const result = lines.slice(0, MAX_RESULT_LINES).join("\n");
|
|
143
|
+
const truncated =
|
|
144
|
+
lines.length > MAX_RESULT_LINES || Buffer.byteLength(result, "utf8") > MAX_RESULT_BYTES;
|
|
145
|
+
if (!truncated) return result;
|
|
146
|
+
|
|
147
|
+
const marker = "[Antigravity result truncated to PI limits]";
|
|
148
|
+
const markerBytes = Buffer.byteLength(marker, "utf8") + 1;
|
|
149
|
+
const contentLimit = Math.max(0, MAX_RESULT_BYTES - markerBytes);
|
|
150
|
+
const contentLines = result.split(/\r?\n/).slice(0, MAX_RESULT_LINES - 1);
|
|
151
|
+
const content = truncateUtf8(contentLines.join("\n"), contentLimit);
|
|
152
|
+
return content ? `${content}\n${marker}` : marker;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function truncateUtf8(value: string, maxBytes: number): string {
|
|
156
|
+
let bytes = 0;
|
|
157
|
+
let result = "";
|
|
158
|
+
for (const character of value) {
|
|
159
|
+
const characterBytes = Buffer.byteLength(character, "utf8");
|
|
160
|
+
if (bytes + characterBytes > maxBytes) break;
|
|
161
|
+
result += character;
|
|
162
|
+
bytes += characterBytes;
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function safeMarkdown(value: string): string {
|
|
168
|
+
return value
|
|
169
|
+
.replaceAll("[", "\\[")
|
|
170
|
+
.replaceAll("]", "\\]")
|
|
171
|
+
.replace(/[\r\n]/g, " ");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function toPiUsage(usage: AntigravityUsage): Usage {
|
|
175
|
+
const input = usage.inputTokens ?? 0;
|
|
176
|
+
const output = usage.outputTokens ?? 0;
|
|
177
|
+
const totalTokens = usage.totalTokens ?? input + output;
|
|
178
|
+
return {
|
|
179
|
+
input,
|
|
180
|
+
output,
|
|
181
|
+
cacheRead: 0,
|
|
182
|
+
cacheWrite: 0,
|
|
183
|
+
totalTokens,
|
|
184
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
185
|
+
};
|
|
186
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TSchema } from "typebox";
|
|
2
|
+
import type { CuratedModel } from "../../types.ts";
|
|
3
|
+
import { buildAntigravityRunSchema } from "./input.ts";
|
|
4
|
+
|
|
5
|
+
/** Canonical Antigravity tool name. */
|
|
6
|
+
export const ANTIGRAVITY_RUN_TOOL_NAME = "antigravity_run";
|
|
7
|
+
/** Human-facing Antigravity tool label. */
|
|
8
|
+
export const ANTIGRAVITY_RUN_TOOL_LABEL = "Antigravity Run";
|
|
9
|
+
|
|
10
|
+
/** Build the provider-facing parameters from one immutable Model Catalogue. */
|
|
11
|
+
export function buildAntigravityRunParameters(catalogue: readonly CuratedModel[]): TSchema {
|
|
12
|
+
return buildAntigravityRunSchema(catalogue);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Canonical machine-readable metadata for antigravity_run. */
|
|
16
|
+
export const antigravityRunSpec = {
|
|
17
|
+
name: ANTIGRAVITY_RUN_TOOL_NAME,
|
|
18
|
+
label: ANTIGRAVITY_RUN_TOOL_LABEL,
|
|
19
|
+
executionMode: "parallel",
|
|
20
|
+
} as const;
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** The four model choices SuPi may expose to a new Antigravity Run. */
|
|
2
|
+
export const CURATED_MODELS = Object.freeze([
|
|
3
|
+
"gemini-3.8-flash-low",
|
|
4
|
+
"gemini-3.8-flash-medium",
|
|
5
|
+
"gemini-3.8-flash-high",
|
|
6
|
+
"gemini-3.1-pro-high",
|
|
7
|
+
] as const);
|
|
8
|
+
|
|
9
|
+
/** A model in the discovered Model Catalogue. */
|
|
10
|
+
export type CuratedModel = (typeof CURATED_MODELS)[number];
|
|
11
|
+
|
|
12
|
+
/** Return whether a value is one of the four curated model IDs. */
|
|
13
|
+
export function isCuratedModel(value: unknown): value is CuratedModel {
|
|
14
|
+
return typeof value === "string" && CURATED_MODELS.includes(value as CuratedModel);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Workspace choice made by the caller. */
|
|
18
|
+
export type WorkspaceAccess = boolean;
|
|
19
|
+
|
|
20
|
+
/** A source returned by Antigravity's structured answer. */
|
|
21
|
+
export interface AntigravitySource {
|
|
22
|
+
title: string;
|
|
23
|
+
url: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A workspace path returned by Antigravity's structured answer. */
|
|
27
|
+
export interface AntigravityWorkspaceEvidence {
|
|
28
|
+
path: string;
|
|
29
|
+
summary: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The exact structured answer required from Antigravity. */
|
|
33
|
+
export interface AntigravityAnswer {
|
|
34
|
+
answer: string;
|
|
35
|
+
sources: AntigravitySource[];
|
|
36
|
+
workspaceEvidence: AntigravityWorkspaceEvidence[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Bounded token accounting reported by the Antigravity CLI. */
|
|
40
|
+
export interface AntigravityUsage {
|
|
41
|
+
inputTokens?: number;
|
|
42
|
+
outputTokens?: number;
|
|
43
|
+
totalTokens?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** A validated answer reference classified by its execution evidence. */
|
|
47
|
+
export interface ClassifiedSource extends AntigravitySource {
|
|
48
|
+
evidence: "observed" | "claimed";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** A validated workspace reference classified by its execution evidence. */
|
|
52
|
+
export interface ClassifiedWorkspaceEvidence extends AntigravityWorkspaceEvidence {
|
|
53
|
+
evidence: "observed" | "claimed";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Safe process facts collected without retaining stream events. */
|
|
57
|
+
export interface AntigravityExecutionFacts {
|
|
58
|
+
answer: AntigravityAnswer;
|
|
59
|
+
conversationId: string;
|
|
60
|
+
usage?: AntigravityUsage;
|
|
61
|
+
observedToolNames: string[];
|
|
62
|
+
observedToolCounts: Record<string, number>;
|
|
63
|
+
successfulToolNames: string[];
|
|
64
|
+
permissionDenials: number;
|
|
65
|
+
observedSourceHashes: string[];
|
|
66
|
+
observedWorkspacePathHashes: string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** State persisted for one opaque Conversation Handle. */
|
|
70
|
+
export interface ConversationHandleRecord {
|
|
71
|
+
handle: string;
|
|
72
|
+
rawAntigravityId: string;
|
|
73
|
+
model: CuratedModel;
|
|
74
|
+
canonicalWorkingDirectory: string;
|
|
75
|
+
workspaceAccess: WorkspaceAccess;
|
|
76
|
+
cliVersion: string;
|
|
77
|
+
status: "active" | "retired";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Human and model-visible details for one completed Antigravity Run. */
|
|
81
|
+
export interface AntigravityRunResultDetails {
|
|
82
|
+
model: CuratedModel;
|
|
83
|
+
workingDirectoryKind: "consultation" | "workspace";
|
|
84
|
+
canonicalWorkingDirectory: string;
|
|
85
|
+
workspaceAccess: WorkspaceAccess;
|
|
86
|
+
cliVersion: string;
|
|
87
|
+
durationMs: number;
|
|
88
|
+
usage?: AntigravityUsage;
|
|
89
|
+
observedToolNames: string[];
|
|
90
|
+
observedToolCounts: Record<string, number>;
|
|
91
|
+
permissionDenials: number;
|
|
92
|
+
webUsed: boolean;
|
|
93
|
+
workspaceUsed: boolean;
|
|
94
|
+
ambientProjectGuidanceAvailable: boolean;
|
|
95
|
+
activeProjectHookWarning?: string;
|
|
96
|
+
handle: string;
|
|
97
|
+
rawAntigravityId: string;
|
|
98
|
+
handleState: "active";
|
|
99
|
+
observedSources: ClassifiedSource[];
|
|
100
|
+
claimedSources: ClassifiedSource[];
|
|
101
|
+
observedWorkspaceEvidence: ClassifiedWorkspaceEvidence[];
|
|
102
|
+
claimedWorkspaceEvidence: ClassifiedWorkspaceEvidence[];
|
|
103
|
+
warnings: string[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Optional callback for safe progress activity. */
|
|
107
|
+
export type AntigravityProgressCallback = (activity: string) => void;
|