@ory/antigravity 0.10.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.
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Main CLI for @ory/antigravity.
5
+ *
6
+ * Usage:
7
+ * npx ory-antigravity install Install the Ory plugin into Antigravity
8
+ * npx ory-antigravity uninstall Remove the plugin
9
+ * npx ory-antigravity configure Set or view Ory credentials
10
+ * npx ory-antigravity setup [--project-dir] Write the bundle into .agents (fallback)
11
+ * npx ory-antigravity status Show plugin status
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ const path = __importStar(require("node:path"));
48
+ const fs = __importStar(require("node:fs"));
49
+ const node_child_process_1 = require("node:child_process");
50
+ const argus_1 = require("@ory/argus");
51
+ const assets_js_1 = require("./assets.js");
52
+ /** Bundle dir handed to `agy plugin install`. */
53
+ const BUNDLE_DIR = path.resolve(__dirname, "..", "..", "antigravity-plugin");
54
+ const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
55
+ /** Antigravity CLI binary name. */
56
+ const AGY = "agy";
57
+ /** On Windows, npm-installed CLIs are `.cmd` shims found only via the shell. */
58
+ const SPAWN_VIA_SHELL = process.platform === "win32";
59
+ function agyAvailable() {
60
+ return ((0, node_child_process_1.spawnSync)(AGY, ["--version"], { stdio: "pipe", shell: SPAWN_VIA_SHELL }).status === 0);
61
+ }
62
+ function main() {
63
+ const [command, ...args] = process.argv.slice(2);
64
+ switch (command) {
65
+ case "install":
66
+ install(args);
67
+ postInstallPermissions("ory-antigravity", "antigravity").then(() => process.exit(0), (err) => {
68
+ console.error(err.message ?? err);
69
+ process.exit(1);
70
+ });
71
+ break;
72
+ case "uninstall":
73
+ uninstall(args);
74
+ break;
75
+ case "configure":
76
+ (0, argus_1.runConfigureCommand)("ory-antigravity", args);
77
+ break;
78
+ case "agent":
79
+ (0, argus_1.runAgentCommand)("ory-antigravity", args).then((code) => process.exit(code), (err) => {
80
+ console.error(err.message ?? err);
81
+ process.exit(1);
82
+ });
83
+ break;
84
+ case "permissions":
85
+ (0, argus_1.runPermissionsCommand)("ory-antigravity", "antigravity", args).then((code) => process.exit(code), (err) => {
86
+ console.error(err.message ?? err);
87
+ process.exit(1);
88
+ });
89
+ break;
90
+ case "setup":
91
+ require("./setup.js");
92
+ break;
93
+ case "status":
94
+ status().then(() => process.exit(0), (err) => {
95
+ console.error(err.message ?? err);
96
+ process.exit(1);
97
+ });
98
+ break;
99
+ case "local":
100
+ (0, argus_1.runLocalCommand)("ory-antigravity", args).catch((err) => {
101
+ console.error(err.message ?? err);
102
+ process.exit(1);
103
+ });
104
+ break;
105
+ case "help":
106
+ case "--help":
107
+ case "-h":
108
+ case undefined:
109
+ help();
110
+ break;
111
+ default:
112
+ console.error(`Unknown command: ${command}`);
113
+ help();
114
+ process.exit(1);
115
+ }
116
+ }
117
+ function install(args) {
118
+ // Materialize the bundle (plugin.json + hooks.json + mcp_config.json +
119
+ // skills + commands) before handing it to `agy plugin install`.
120
+ console.log("Materializing the Ory plugin bundle...");
121
+ (0, assets_js_1.installAntigravityOryAssets)(BUNDLE_DIR);
122
+ if (agyAvailable()) {
123
+ console.log("Installing Ory plugin into Antigravity...");
124
+ const result = (0, node_child_process_1.spawnSync)(AGY, ["plugin", "install", BUNDLE_DIR], {
125
+ stdio: "inherit",
126
+ shell: SPAWN_VIA_SHELL,
127
+ });
128
+ if (result.status === 0) {
129
+ console.log("");
130
+ console.log("Ory plugin installed.");
131
+ printQuickstart();
132
+ (0, argus_1.printEnvHelp)("ory-antigravity");
133
+ return;
134
+ }
135
+ console.error("");
136
+ console.error("`agy plugin install` failed. Falling back to manual setup...");
137
+ }
138
+ else {
139
+ console.log("Antigravity CLI (agy) not found on PATH. Using manual setup...");
140
+ }
141
+ manualSetup(args);
142
+ }
143
+ function uninstall(args) {
144
+ if (agyAvailable()) {
145
+ console.log("Uninstalling Ory plugin from Antigravity...");
146
+ const result = (0, node_child_process_1.spawnSync)(AGY, ["plugin", "uninstall", "ory"], {
147
+ stdio: "inherit",
148
+ shell: SPAWN_VIA_SHELL,
149
+ });
150
+ if (result.status === 0)
151
+ return;
152
+ console.error("`agy plugin uninstall` failed. Falling back to manual removal...");
153
+ }
154
+ process.argv = ["node", "setup.js", "--uninstall", ...args];
155
+ require("./setup.js");
156
+ }
157
+ function manualSetup(args) {
158
+ process.argv = ["node", "setup.js", ...args];
159
+ require("./setup.js");
160
+ }
161
+ async function postInstallPermissions(binName, harness) {
162
+ const bootstrapped = await (0, argus_1.maybeAutoBootstrap)(binName, harness);
163
+ (0, argus_1.printPermissionsOnboardingHelp)(binName, harness, {
164
+ bootstrappedAutomatically: bootstrapped,
165
+ });
166
+ }
167
+ function printQuickstart() {
168
+ console.log("");
169
+ console.log("Quickstart:");
170
+ console.log(" 1. Inside Antigravity, run /ory:local-up to start a local Ory stack.");
171
+ console.log(' 2. Ask Antigravity "add Ory auth to this app" to scaffold login/signup.');
172
+ console.log(" 3. Set ORY_USER_LOGIN=true to enable the interactive PKCE browser login.");
173
+ }
174
+ async function status() {
175
+ await (0, argus_1.runStatusCommand)("ory-antigravity", "antigravity", {
176
+ title: "Google Antigravity",
177
+ printPluginSection: () => {
178
+ console.log("Plugin bundle:");
179
+ console.log(` Directory: ${BUNDLE_DIR}`);
180
+ console.log(` Manifest: ${fs.existsSync(path.join(BUNDLE_DIR, "plugin.json")) ? "found" : "MISSING"}`);
181
+ console.log(` Hook script: ${fs.existsSync(path.join(PACKAGE_ROOT, "dist", "hook.js")) ? "built" : "NOT BUILT (run pnpm build)"}`);
182
+ console.log(` agy CLI: ${agyAvailable() ? "found" : "not on PATH"}`);
183
+ },
184
+ });
185
+ }
186
+ function help() {
187
+ console.log(`
188
+ ory-antigravity — Ory plugin for Google Antigravity
189
+
190
+ Usage:
191
+ npx ory-antigravity <command> [options]
192
+
193
+ Commands:
194
+ install Install the Ory plugin into Antigravity (agy plugin install)
195
+ uninstall Remove the Ory plugin from Antigravity
196
+ configure Set or view Ory project URL and credentials
197
+ agent <cmd> Manage the agent's OAuth2 (DCR) identity (status, unregister)
198
+ permissions <cmd> Manage permission mode and tool permissions (status, bootstrap, observe, enforce)
199
+ setup [--project-dir] Write the plugin bundle into .agents directly (fallback)
200
+ status Show plugin status and configuration
201
+ local <cmd> Manage local Ory dev environment (up, down, status, seed, ...)
202
+
203
+ After installing, the plugin hooks into these Antigravity lifecycle events:
204
+ SessionStart Verify the user and agent identities via Ory
205
+ PreToolUse Check tool permissions via Ory Permissions
206
+ PostToolUse Audit log tool executions
207
+
208
+ Examples:
209
+ npx ory-antigravity install # Install plugin
210
+ npx ory-antigravity status # Check configuration
211
+ npx ory-antigravity uninstall # Remove plugin
212
+ `);
213
+ }
214
+ main();
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Setup CLI for the Ory Google Antigravity plugin (manual fallback).
4
+ *
5
+ * When the `agy` CLI isn't available to register the plugin natively,
6
+ * this writes the Ory plugin bundle directly into the workspace's
7
+ * `.agents/` directory — the verified workspace location Antigravity
8
+ * reads hooks, MCP config, skills, and commands from.
9
+ *
10
+ * Usage:
11
+ * npx ory-antigravity-setup # write into ./.agents
12
+ * npx ory-antigravity-setup --project-dir /repo
13
+ * npx ory-antigravity-setup --print # print bundle paths, don't write
14
+ * npx ory-antigravity-setup --uninstall # remove Ory bundle files
15
+ */
16
+ export {};
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Setup CLI for the Ory Google Antigravity plugin (manual fallback).
5
+ *
6
+ * When the `agy` CLI isn't available to register the plugin natively,
7
+ * this writes the Ory plugin bundle directly into the workspace's
8
+ * `.agents/` directory — the verified workspace location Antigravity
9
+ * reads hooks, MCP config, skills, and commands from.
10
+ *
11
+ * Usage:
12
+ * npx ory-antigravity-setup # write into ./.agents
13
+ * npx ory-antigravity-setup --project-dir /repo
14
+ * npx ory-antigravity-setup --print # print bundle paths, don't write
15
+ * npx ory-antigravity-setup --uninstall # remove Ory bundle files
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ const fs = __importStar(require("node:fs"));
52
+ const path = __importStar(require("node:path"));
53
+ const argus_1 = require("@ory/argus");
54
+ const assets_js_1 = require("./assets.js");
55
+ function getAgentsDir(args) {
56
+ return path.join(args.projectDir, ".agents");
57
+ }
58
+ function main() {
59
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
60
+ (0, argus_1.printSetupHelp)("ory-antigravity-setup", "Google Antigravity");
61
+ process.exit(0);
62
+ }
63
+ const args = (0, argus_1.parseSetupArgs)();
64
+ const agentsDir = getAgentsDir(args);
65
+ if (args.print) {
66
+ console.log(`Would write the Ory plugin bundle to ${agentsDir}:`);
67
+ console.log(" plugin.json, hooks.json, mcp_config.json, skills/, commands/ory/");
68
+ return;
69
+ }
70
+ if (args.uninstall) {
71
+ if (!fs.existsSync(agentsDir)) {
72
+ console.log("No .agents directory found. Nothing to uninstall.");
73
+ return;
74
+ }
75
+ (0, assets_js_1.uninstallAntigravityOryAssets)(agentsDir);
76
+ console.log(`Removed Ory plugin bundle from ${agentsDir}`);
77
+ return;
78
+ }
79
+ (0, assets_js_1.installAntigravityOryAssets)(agentsDir);
80
+ console.log(`Ory plugin bundle installed to ${agentsDir}`);
81
+ (0, argus_1.printNextSteps)("Google Antigravity", "npx ory-antigravity-setup --uninstall");
82
+ }
83
+ main();
@@ -0,0 +1,16 @@
1
+ import { OryAgentClient, ensureUserAuthenticated, ensureAgentIdentity } from "@ory/argus";
2
+ import type { AntigravityHookInput, AntigravityHookOutput } from "./types.js";
3
+ export interface HandleHookEventDeps {
4
+ /** Test injection point for the user login flow. */
5
+ userLogin?: typeof ensureUserAuthenticated;
6
+ /** Test injection point for the agent identity gate. */
7
+ agentGate?: typeof ensureAgentIdentity;
8
+ }
9
+ /**
10
+ * Route an Antigravity hook event to the appropriate Ory integration.
11
+ *
12
+ * Note: on `PreToolUse` the returned output ALWAYS sets `allow_tool`
13
+ * explicitly — Antigravity blocks the tool otherwise. All other events
14
+ * return `ALLOW` (harmless) or an empty/system-message output.
15
+ */
16
+ export declare function handleHookEvent(input: AntigravityHookInput, client: OryAgentClient, deps?: HandleHookEventDeps): Promise<AntigravityHookOutput>;
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleHookEvent = handleHookEvent;
4
+ const argus_1 = require("@ory/argus");
5
+ const HARNESS = "antigravity";
6
+ /** Allow a tool through (fail-closed harness ⇒ always be explicit). */
7
+ const ALLOW = { allow_tool: true };
8
+ /**
9
+ * Route an Antigravity hook event to the appropriate Ory integration.
10
+ *
11
+ * Note: on `PreToolUse` the returned output ALWAYS sets `allow_tool`
12
+ * explicitly — Antigravity blocks the tool otherwise. All other events
13
+ * return `ALLOW` (harmless) or an empty/system-message output.
14
+ */
15
+ async function handleHookEvent(input, client, deps = {}) {
16
+ const event = input.hook_event_name;
17
+ client.logger.debug("hook.received", {
18
+ event,
19
+ sessionId: input.session_id,
20
+ toolName: input.tool_name,
21
+ });
22
+ client.tracer.setContext({
23
+ traceId: (0, argus_1.deriveTraceId)(input.session_id),
24
+ sessionId: input.session_id,
25
+ });
26
+ try {
27
+ switch (event) {
28
+ case "SessionStart":
29
+ return await handleSessionStart(input, client, deps);
30
+ case "PreToolUse":
31
+ return await handlePreToolUse(input, client);
32
+ case "PostToolUse":
33
+ return await handlePostToolUse(input, client);
34
+ case "Notification":
35
+ return handleNotification(input, client);
36
+ case "Stop":
37
+ case "SessionEnd":
38
+ return handleSessionEnd(input, client);
39
+ default:
40
+ client.logger.debug("hook.passthrough", { event });
41
+ client.tracer.record("hook.passthrough", "skipped", {
42
+ attributes: { event },
43
+ });
44
+ return ALLOW;
45
+ }
46
+ }
47
+ finally {
48
+ client.tracer.clearContext();
49
+ }
50
+ }
51
+ // ─── SessionStart ───────────────────────────────────────────────────
52
+ async function handleSessionStart(input, client, deps) {
53
+ client.logger.info("lifecycle.session_start", {
54
+ sessionId: input.session_id,
55
+ model: input.model,
56
+ source: input.source,
57
+ });
58
+ client.tracer.record("session.start", "ok", {
59
+ attributes: { model: input.model, source: input.source },
60
+ });
61
+ // User login (interactive PKCE / token refresh). Advisory on
62
+ // Antigravity: SessionStart has no verified hard-block channel
63
+ // (`allow_tool` only governs PreToolUse), so allowBlock is false — the
64
+ // gate runs, refreshes tokens, and emits the audit span, but the
65
+ // session always proceeds. Tool-call enforcement is unaffected.
66
+ const userGate = deps.userLogin ?? argus_1.ensureUserAuthenticated;
67
+ const decision = await userGate(client, {
68
+ binName: "ory-antigravity",
69
+ harness: HARNESS,
70
+ allowBlock: false,
71
+ });
72
+ // Resolve the agent identity regardless of how user auth went — never
73
+ // blocks; attaches the agent's bearer token to outgoing Ory calls.
74
+ const agentGate = deps.agentGate ?? argus_1.ensureAgentIdentity;
75
+ await agentGate(client, { projectUrl: (0, argus_1.resolveConfig)().projectUrl, harness: HARNESS });
76
+ // Record the user→agent delegation (audit-trail only; fail-open).
77
+ await recordUserDelegatesAgent(client);
78
+ if (decision.mode !== "disabled") {
79
+ return {};
80
+ }
81
+ // Legacy fallthrough: honor pre-supplied tokens for backward compat.
82
+ const resolved = (0, argus_1.resolveConfig)();
83
+ if (resolved.auditOnly || !resolved.projectUrl) {
84
+ return {};
85
+ }
86
+ const sessionToken = process.env.ORY_SESSION_TOKEN;
87
+ const oauth2Token = process.env.ORY_OAUTH2_TOKEN;
88
+ if (sessionToken) {
89
+ await verifySessionToken(sessionToken, client);
90
+ }
91
+ else if (oauth2Token) {
92
+ await verifyOAuth2Token(oauth2Token, client);
93
+ }
94
+ else {
95
+ client.logger.warn("session.no_credentials", {
96
+ message: "No user login enabled and neither ORY_SESSION_TOKEN nor " +
97
+ "ORY_OAUTH2_TOKEN is set. Skipping authentication.",
98
+ });
99
+ }
100
+ return {};
101
+ }
102
+ async function verifySessionToken(token, client) {
103
+ try {
104
+ const session = await client.verifySession(token);
105
+ if (!session.active) {
106
+ client.logger.warn("session.inactive", {
107
+ message: "Ory session is not active. Re-authenticate to enable auth checks.",
108
+ });
109
+ }
110
+ }
111
+ catch (err) {
112
+ const oryErr = err;
113
+ client.logger.warn("session.verify_failed", {
114
+ code: oryErr.code,
115
+ message: oryErr.message,
116
+ });
117
+ }
118
+ }
119
+ async function verifyOAuth2Token(token, client) {
120
+ try {
121
+ const tokenInfo = await client.introspectToken(token);
122
+ if (!tokenInfo.active) {
123
+ client.logger.warn("oauth2.token_inactive", {
124
+ message: "Ory OAuth2 token is not active.",
125
+ });
126
+ return;
127
+ }
128
+ client.logger.info("oauth2.session_authenticated", {
129
+ clientId: tokenInfo.clientId,
130
+ subject: tokenInfo.subject,
131
+ scope: tokenInfo.scope,
132
+ });
133
+ }
134
+ catch (err) {
135
+ const oryErr = err;
136
+ client.logger.warn("oauth2.introspect_failed", {
137
+ code: oryErr.code,
138
+ message: oryErr.message,
139
+ });
140
+ }
141
+ }
142
+ // ─── PreToolUse ─────────────────────────────────────────────────────
143
+ async function handlePreToolUse(input, client) {
144
+ const toolName = input.tool_name ?? "unknown";
145
+ client.logger.info("lifecycle.pre_tool_use", {
146
+ sessionId: input.session_id,
147
+ toolName,
148
+ toolInput: input.tool_input,
149
+ });
150
+ const inputSummary = (0, argus_1.summarizeToolInput)(toolName, input.tool_input);
151
+ // Audit-only mode: log the invocation, skip the permission check, allow.
152
+ if ((0, argus_1.resolveConfig)().auditOnly) {
153
+ client.tracer.record("tool.invoke", "ok", {
154
+ attributes: { toolName, ...inputSummary },
155
+ });
156
+ return ALLOW;
157
+ }
158
+ const subject = (0, argus_1.resolveUserSubject)(client, `session:${input.session_id}`);
159
+ const subjectId = (0, argus_1.subjectLabel)(subject);
160
+ const namespace = resolveNamespace();
161
+ try {
162
+ const outcome = await (0, argus_1.gateToolCall)(client, {
163
+ harness: HARNESS,
164
+ toolName,
165
+ check: { namespace, object: toolName, relation: "use", ...subject },
166
+ spanAttributes: { toolName },
167
+ });
168
+ // Interactive tools surface UI to the user; gateToolCall already
169
+ // recorded the user.interaction span. Let the harness prompt.
170
+ if (outcome.kind === "interactive") {
171
+ return ALLOW;
172
+ }
173
+ if (outcome.kind === "fail_open") {
174
+ return failOpen(outcome.error, toolName, client);
175
+ }
176
+ const attrs = { toolName, ...inputSummary };
177
+ const decisionAttrs = outcome.spanAttributes;
178
+ if (outcome.kind === "allow") {
179
+ client.tracer.record("tool.invoke", "ok", {
180
+ attributes: { ...attrs, ...decisionAttrs, allowed: true },
181
+ });
182
+ return ALLOW;
183
+ }
184
+ if (outcome.kind === "observe") {
185
+ client.tracer.record("tool.block", "denied", {
186
+ attributes: { ...attrs, ...decisionAttrs, allowed: false, ...(0, argus_1.alertAttributes)(false) },
187
+ });
188
+ client.tracer.record("tool.invoke", "ok", {
189
+ attributes: { ...attrs, ...decisionAttrs, allowed: false, observed: true },
190
+ });
191
+ return ALLOW;
192
+ }
193
+ // deny → block the tool (enforce mode).
194
+ client.tracer.record("tool.block", "denied", {
195
+ attributes: { ...attrs, ...decisionAttrs, allowed: false, ...(0, argus_1.alertAttributes)(true) },
196
+ });
197
+ return {
198
+ allow_tool: false,
199
+ deny_reason: (0, argus_1.formatDenialMessage)({ tool: toolName, subjectId, namespace }),
200
+ };
201
+ }
202
+ catch (err) {
203
+ return failOpen(err, toolName, client);
204
+ }
205
+ }
206
+ // ─── PostToolUse ────────────────────────────────────────────────────
207
+ async function handlePostToolUse(input, client) {
208
+ const toolName = input.tool_name ?? "unknown";
209
+ client.logger.info("lifecycle.post_tool_use", {
210
+ sessionId: input.session_id,
211
+ toolName,
212
+ });
213
+ const failed = input.tool_error !== undefined && input.tool_error !== null;
214
+ if (failed) {
215
+ client.tracer.record("tool.fail", "error", {
216
+ attributes: {
217
+ toolName,
218
+ toolError: typeof input.tool_error === "string"
219
+ ? input.tool_error.slice(0, 500)
220
+ : "[object]",
221
+ ...(0, argus_1.summarizeToolInput)(toolName, input.tool_input),
222
+ },
223
+ });
224
+ return {};
225
+ }
226
+ client.tracer.record("tool.complete", "ok", {
227
+ attributes: {
228
+ toolName,
229
+ ...(0, argus_1.summarizeToolInput)(toolName, input.tool_input),
230
+ ...(0, argus_1.summarizeToolOutput)(toolName, input.tool_response),
231
+ },
232
+ });
233
+ return {};
234
+ }
235
+ // PreInvocation / PostInvocation bracket each agent turn (not user
236
+ // prompts). We don't model them specially — they fall through to the
237
+ // default `hook.passthrough` audit span in handleHookEvent.
238
+ // ─── Notification ───────────────────────────────────────────────────
239
+ function handleNotification(input, client) {
240
+ client.logger.info("lifecycle.notification", {
241
+ sessionId: input.session_id,
242
+ messageLen: input.message?.length,
243
+ });
244
+ client.tracer.record("user.interaction", "ok", {
245
+ attributes: { harness: HARNESS, kind: "notification" },
246
+ });
247
+ return {};
248
+ }
249
+ // ─── Stop / SessionEnd ──────────────────────────────────────────────
250
+ function handleSessionEnd(input, client) {
251
+ client.logger.info("lifecycle.session_end", {
252
+ sessionId: input.session_id,
253
+ reason: input.reason,
254
+ });
255
+ client.tracer.record("session.end", "ok", {
256
+ attributes: { reason: input.reason },
257
+ });
258
+ return {};
259
+ }
260
+ // ─── Helpers ────────────────────────────────────────────────────────
261
+ function resolveNamespace() {
262
+ return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
263
+ }
264
+ /**
265
+ * Write the user→agent delegation tuple. Idempotent and fail-open:
266
+ * needs both principal subjects; any error is logged and swallowed.
267
+ */
268
+ async function recordUserDelegatesAgent(client) {
269
+ const user = client.userPrincipal.subject;
270
+ const agent = client.agentPrincipal.subject;
271
+ if (!user || !agent) {
272
+ client.logger.debug("delegation.skip", {
273
+ reason: "missing principal",
274
+ hasUser: !!user,
275
+ hasAgent: !!agent,
276
+ });
277
+ return;
278
+ }
279
+ try {
280
+ await client.createRelationship({
281
+ namespace: resolveNamespace(),
282
+ object: `agent:${agent}`,
283
+ relation: "delegate",
284
+ subjectId: `user:${user}`,
285
+ }, { spanAttributes: { delegation: "user-to-agent" } });
286
+ }
287
+ catch (err) {
288
+ const oryErr = err;
289
+ client.logger.warn("delegation.user_to_agent.failed", {
290
+ code: oryErr.code,
291
+ message: oryErr.message,
292
+ });
293
+ }
294
+ }
295
+ /**
296
+ * Fail-open on a permission-check error: log and allow the tool. Critical
297
+ * on Antigravity — returning anything other than `allow_tool: true` here
298
+ * would block the user on a transient Ory/network blip.
299
+ */
300
+ function failOpen(oryErr, toolName, client) {
301
+ if (oryErr.code === "network_error") {
302
+ client.logger.warn("permission.network_error", {
303
+ toolName,
304
+ message: "Ory unreachable, failing open",
305
+ });
306
+ }
307
+ else if (oryErr.code === "rate_limited") {
308
+ client.logger.warn("permission.rate_limited", {
309
+ toolName,
310
+ message: "Ory rate limited, failing open",
311
+ });
312
+ }
313
+ else {
314
+ client.logger.error("permission.check.error", {
315
+ toolName,
316
+ code: oryErr.code,
317
+ message: oryErr.message,
318
+ });
319
+ }
320
+ return ALLOW;
321
+ }
package/dist/hook.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Google Antigravity hook entry point.
4
+ *
5
+ * Antigravity invokes this script as a subprocess for each hook event.
6
+ * Input: JSON on stdin.
7
+ * Output: JSON on stdout.
8
+ *
9
+ * FAIL-CLOSED CONTRACT (the reason this differs from every other plugin):
10
+ * on a `PreToolUse` event, Antigravity blocks the tool unless the hook
11
+ * returns `{"allow_tool": true}`. A crash, a timeout, malformed output, or
12
+ * a non-zero exit are all treated as "block". To preserve this repo's
13
+ * fail-open guarantee, EVERY error path here emits `{"allow_tool": true}`
14
+ * and exits 0 — a transient Ory/network failure must never wedge the
15
+ * user's tools. Emitting `allow_tool` on non-tool events is harmless
16
+ * (the harness ignores it), so the safe default works for all events.
17
+ */
18
+ export {};