@ory/codex 0.10.0 → 0.11.1

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/README.md CHANGED
@@ -30,8 +30,8 @@ If you're just exploring, do the Quickstart first.
30
30
  No prior `npm install` required:
31
31
 
32
32
  ```bash
33
- npx @ory/codex install # registers the Ory marketplace + plugin with `codex`
34
- npx @ory/codex uninstall # removes both
33
+ npx -y -p @ory/codex ory-codex install # registers the Ory marketplace + plugin with `codex`
34
+ npx -y -p @ory/codex ory-codex uninstall # removes both
35
35
  ```
36
36
 
37
37
  Then confirm everything landed:
@@ -255,7 +255,7 @@ Highlights:
255
255
  - **`ory-local-up` fails.** Make sure Docker is running and ports `4455` (login UI), `4000`, `4100`, and `16686` are free.
256
256
  - **PKCE login loops.** Clear persisted state with `npx -y -p @ory/codex ory-codex agent unregister` and retry.
257
257
  - **`npx` fetches an old version.** Force a fresh fetch: `npx -y -p @ory/codex@latest ory-codex …`.
258
- - **`npm install … ENOVERSIONS`.** If your `~/.npmrc` sets `min-release-age`, npm filters out package versions newer than that threshold. Override per-call with `npm_config_min_release_age=0 npx @ory/codex install`.
258
+ - **`npm install … ENOVERSIONS`.** If your `~/.npmrc` sets `min-release-age`, npm filters out package versions newer than that threshold. Override per-call with `npm_config_min_release_age=0 npx -y -p @ory/codex ory-codex install`.
259
259
  - **`codex doctor` reports `ory-mcp-server is not resolvable`.** The bundled MCP server resolves via `npx -y -p @ory/mcp-server` on demand — make sure `npm`/`npx` is on PATH. The first session start fetches the server tarball; subsequent sessions reuse the cache.
260
260
  - **Need more signal.** Set `ORY_AGENT_DEBUG=true` and `ORY_AGENT_LOG_FILE=/tmp/ory.log` to capture structured logs.
261
261
 
@@ -14,3 +14,13 @@ export interface HandleHookEventDeps {
14
14
  * `[features] codex_hooks = true` in `~/.codex/config.toml`.
15
15
  */
16
16
  export declare function handleHookEvent(input: CodexHookInput, client: OryAgentClient, deps?: HandleHookEventDeps): Promise<CodexHookOutput>;
17
+ /**
18
+ * Whether a hook output carries anything Codex needs to see on stdout.
19
+ *
20
+ * The hook entry point handles `decision === "block"` (exit code 2 + stdout)
21
+ * before consulting this. Everything else with at least one populated field
22
+ * must be written to stdout — most importantly `hookSpecificOutput`, which
23
+ * carries the PermissionRequest allow/deny decision. Empty outputs stay
24
+ * silent to keep stdout clean for the harness.
25
+ */
26
+ export declare function shouldEmitOutput(output: CodexHookOutput): boolean;
package/dist/handlers.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleHookEvent = handleHookEvent;
4
+ exports.shouldEmitOutput = shouldEmitOutput;
4
5
  const argus_1 = require("@ory/argus");
5
6
  /**
6
7
  * Route a Codex hook event to the appropriate Ory integration.
@@ -68,6 +69,12 @@ async function handleSessionStart(input, client, deps) {
68
69
  // attaches the agent's bearer token to outgoing Ory API calls.
69
70
  const agentGate = deps.agentGate ?? argus_1.ensureAgentIdentity;
70
71
  await agentGate(client, { projectUrl: (0, argus_1.resolveConfig)().projectUrl, harness: "codex" });
72
+ // Record the user→agent delegation so the audit trail captures that
73
+ // this user authorized this agent for this session. Best-effort:
74
+ // requires both principals to be populated, and any failure is logged
75
+ // and swallowed (fail-open — delegation tracking is for audit, not
76
+ // enforcement).
77
+ await recordUserDelegatesAgent(client);
71
78
  if (!decision.proceed) {
72
79
  return { decision: "block", reason: decision.reason };
73
80
  }
@@ -416,9 +423,55 @@ async function handleStop(input, client) {
416
423
  return {};
417
424
  }
418
425
  // ─── Helpers ────────────────────────────────────────────────────────
426
+ /**
427
+ * Whether a hook output carries anything Codex needs to see on stdout.
428
+ *
429
+ * The hook entry point handles `decision === "block"` (exit code 2 + stdout)
430
+ * before consulting this. Everything else with at least one populated field
431
+ * must be written to stdout — most importantly `hookSpecificOutput`, which
432
+ * carries the PermissionRequest allow/deny decision. Empty outputs stay
433
+ * silent to keep stdout clean for the harness.
434
+ */
435
+ function shouldEmitOutput(output) {
436
+ return Object.values(output).some((value) => value !== undefined);
437
+ }
419
438
  function resolveNamespace() {
420
439
  return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
421
440
  }
441
+ /**
442
+ * Write the user→agent delegation tuple. Idempotent and fail-open:
443
+ * requires both principal subjects to be populated; any error (including
444
+ * an unconfigured projectUrl, which manifests as a network_error) is
445
+ * logged and swallowed. Purely audit-trail data — does not affect
446
+ * enforcement.
447
+ */
448
+ async function recordUserDelegatesAgent(client) {
449
+ const user = client.userPrincipal.subject;
450
+ const agent = client.agentPrincipal.subject;
451
+ if (!user || !agent) {
452
+ client.logger.debug("delegation.skip", {
453
+ reason: "missing principal",
454
+ hasUser: !!user,
455
+ hasAgent: !!agent,
456
+ });
457
+ return;
458
+ }
459
+ try {
460
+ await client.createRelationship({
461
+ namespace: resolveNamespace(),
462
+ object: `agent:${agent}`,
463
+ relation: "delegate",
464
+ subjectId: `user:${user}`,
465
+ }, { spanAttributes: { delegation: "user-to-agent" } });
466
+ }
467
+ catch (err) {
468
+ const oryErr = err;
469
+ client.logger.warn("delegation.user_to_agent.failed", {
470
+ code: oryErr.code,
471
+ message: oryErr.message,
472
+ });
473
+ }
474
+ }
422
475
  function handlePermissionError(oryErr, toolName, client) {
423
476
  if (oryErr.code === "network_error") {
424
477
  client.logger.warn("permission.network_error", {
package/dist/hook.js CHANGED
@@ -62,7 +62,7 @@ async function main() {
62
62
  await client.tracer.shutdown();
63
63
  process.exit(2);
64
64
  }
65
- if (output.oryConfigRequired || output.message) {
65
+ if ((0, handlers_js_1.shouldEmitOutput)(output)) {
66
66
  process.stdout.write(JSON.stringify(output));
67
67
  }
68
68
  await client.tracer.shutdown();
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { handleHookEvent } from "./handlers.js";
1
+ export { handleHookEvent, shouldEmitOutput } from "./handlers.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleHookEvent = void 0;
3
+ exports.shouldEmitOutput = exports.handleHookEvent = void 0;
4
4
  var handlers_js_1 = require("./handlers.js");
5
5
  Object.defineProperty(exports, "handleHookEvent", { enumerable: true, get: function () { return handlers_js_1.handleHookEvent; } });
6
+ Object.defineProperty(exports, "shouldEmitOutput", { enumerable: true, get: function () { return handlers_js_1.shouldEmitOutput; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ory/codex",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Ory plugin for Codex: scaffolding skills, a local Ory instance, and authentication, authorization, and audit for every tool call",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://ory.com",
@@ -66,7 +66,7 @@
66
66
  "marketplace"
67
67
  ],
68
68
  "dependencies": {
69
- "@ory/argus": "0.10.0"
69
+ "@ory/argus": "0.11.1"
70
70
  },
71
71
  "engines": {
72
72
  "node": ">=22"