@ory/argus 1.2.3 → 1.3.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.
Files changed (52) hide show
  1. package/README.md +25 -0
  2. package/assets/skills/ory-build-agent/SKILL.md +1 -1
  3. package/assets/skills/ory-deploy-agent/SKILL.md +315 -0
  4. package/assets/skills/ory-deploy-openai-e2b/SKILL.md +289 -0
  5. package/assets/skills/ory-e2b-sandbox/SKILL.md +10 -6
  6. package/assets/skills/ory-temporal-worker/SKILL.md +4 -4
  7. package/dist/adapters.d.ts +11 -3
  8. package/dist/adapters.js +55 -13
  9. package/dist/agent-auth.d.ts +18 -7
  10. package/dist/agent-auth.js +120 -10
  11. package/dist/auth-store.d.ts +3 -3
  12. package/dist/auth-store.js +56 -12
  13. package/dist/auth.d.ts +1 -1
  14. package/dist/auth.js +1 -1
  15. package/dist/bash-parser.js +212 -47
  16. package/dist/build-info.json +4 -4
  17. package/dist/cli.js +5 -0
  18. package/dist/client.d.ts +15 -0
  19. package/dist/client.js +24 -7
  20. package/dist/config.d.ts +23 -3
  21. package/dist/config.js +202 -18
  22. package/dist/contract-suite.d.ts +0 -8
  23. package/dist/contract-suite.js +0 -1
  24. package/dist/event-reporter.d.ts +1 -0
  25. package/dist/event-reporter.js +19 -2
  26. package/dist/hook-timeout.d.ts +2 -2
  27. package/dist/hook-timeout.js +2 -2
  28. package/dist/index.d.ts +4 -4
  29. package/dist/index.js +13 -6
  30. package/dist/lifecycle.js +2 -1
  31. package/dist/local/configs.d.ts +2 -4
  32. package/dist/local/configs.js +2 -4
  33. package/dist/local/manager.js +4 -4
  34. package/dist/local/seed.d.ts +4 -4
  35. package/dist/local/seed.js +8 -10
  36. package/dist/permissions.d.ts +3 -3
  37. package/dist/permissions.js +26 -1
  38. package/dist/runtime-credential.d.ts +35 -0
  39. package/dist/runtime-credential.js +252 -1
  40. package/dist/runtime.d.ts +19 -12
  41. package/dist/runtime.js +69 -21
  42. package/dist/setup.js +3 -3
  43. package/dist/skills.d.ts +3 -5
  44. package/dist/skills.js +14 -5
  45. package/dist/subject.d.ts +2 -3
  46. package/dist/subject.js +10 -11
  47. package/dist/testing.d.ts +17 -0
  48. package/dist/testing.js +53 -0
  49. package/dist/tool-catalog.d.ts +17 -15
  50. package/dist/tool-catalog.js +239 -52
  51. package/dist/user-login.js +1 -1
  52. package/package.json +1 -1
package/dist/runtime.js CHANGED
@@ -27,10 +27,10 @@
27
27
  * wired where, so `status` can report the runtime a harness will actually
28
28
  * load, upgrades can rewire, and unreferenced stores can be pruned.
29
29
  *
30
- * A **linked** runtime (`linkedRuntime`) points the shim straight at a
31
- * workspace build instead of a materialized store. That is the development
32
- * loop: `pnpm build` + relaunch, with no publish, no version bump, and no
33
- * registry involved.
30
+ * A **linked** runtime (`linkedRuntime`) points the shim straight at an
31
+ * explicit prebuilt package directory instead of a materialized store. This
32
+ * supports both administrator-managed runtimes and the workspace development
33
+ * loop (`pnpm build` + relaunch), with no registry involved.
34
34
  *
35
35
  * ## The shim must fail open
36
36
  *
@@ -93,6 +93,7 @@ exports.resolveMcpRuntimeEntry = resolveMcpRuntimeEntry;
93
93
  exports.hookShimBody = hookShimBody;
94
94
  exports.writeHookShim = writeHookShim;
95
95
  exports.writeMcpShim = writeMcpShim;
96
+ exports.resolvePluginNodePath = resolvePluginNodePath;
96
97
  exports.hookCommand = hookCommand;
97
98
  exports.isOryRuntimeCommand = isOryRuntimeCommand;
98
99
  exports.mcpCommand = mcpCommand;
@@ -143,16 +144,19 @@ function getManifestPath() {
143
144
  // ─── Runtime targets ───────────────────────────────────────────────
144
145
  /** The npm package providing the MCP server, materialized alongside plugins. */
145
146
  exports.MCP_SERVER_PACKAGE = "@ory/mcp-server";
146
- /** Read a package version from a package directory, if present. */
147
- function readPackageVersion(packageDir) {
147
+ /** Read package metadata from a package directory, if present. */
148
+ function readPackageManifest(packageDir) {
148
149
  try {
149
- const parsed = JSON.parse(fs.readFileSync(path.join(packageDir, "package.json"), "utf-8"));
150
- return typeof parsed.version === "string" ? parsed.version : undefined;
150
+ return JSON.parse(fs.readFileSync(path.join(packageDir, "package.json"), "utf-8"));
151
151
  }
152
152
  catch {
153
153
  return undefined;
154
154
  }
155
155
  }
156
+ function readPackageVersion(packageDir) {
157
+ const version = readPackageManifest(packageDir)?.version;
158
+ return typeof version === "string" ? version : undefined;
159
+ }
156
160
  /**
157
161
  * Default installer: one `npm install --prefix <store> <spec>…`.
158
162
  *
@@ -259,18 +263,23 @@ function materializeRuntime(opts) {
259
263
  };
260
264
  }
261
265
  /**
262
- * Use an already-built package directory as the runtime — the development
263
- * path (`install --link <dir>`), where the workspace build *is* the artifact.
266
+ * Use an explicit prebuilt package directory as the runtime, including a
267
+ * managed installation or workspace build.
264
268
  *
265
269
  * Validates that the directory looks like a built package so a typo surfaces
266
270
  * at install time instead of as hooks that silently never run.
267
271
  */
268
272
  function linkedRuntime(opts) {
269
273
  const packageDir = path.resolve(opts.packageDir);
270
- const version = readPackageVersion(packageDir);
274
+ const manifest = readPackageManifest(packageDir);
275
+ const version = manifest?.version;
271
276
  if (!version) {
272
277
  throw new Error(`Cannot link the Ory runtime: ${packageDir} has no readable package.json.`);
273
278
  }
279
+ if (manifest.name !== opts.packageName) {
280
+ throw new Error(`Cannot link the Ory runtime: ${packageDir}/package.json declares ` +
281
+ `${JSON.stringify(manifest.name)}; expected ${JSON.stringify(opts.packageName)}.`);
282
+ }
274
283
  if (!fs.existsSync(path.join(packageDir, "dist"))) {
275
284
  throw new Error(`Cannot link the Ory runtime: ${packageDir}/dist is missing. Build the ` +
276
285
  `package first (pnpm build).`);
@@ -458,14 +467,49 @@ function writeMcpShim(opts) {
458
467
  return shimPath;
459
468
  }
460
469
  // ─── Rendering into harness config ─────────────────────────────────
470
+ /**
471
+ * Resolve the Node executable persisted in subprocess hook and MCP config.
472
+ * Managed installations can pin an administrator-approved absolute path;
473
+ * ordinary installations retain the portable `node` command lookup.
474
+ */
475
+ function resolvePluginNodePath(env = process.env) {
476
+ const configured = env.ORY_PLUGIN_NODE_PATH;
477
+ if (configured === undefined)
478
+ return "node";
479
+ const nodePath = configured.trim();
480
+ if (!nodePath || !path.isAbsolute(nodePath)) {
481
+ throw new Error("ORY_PLUGIN_NODE_PATH must be a non-empty absolute path when set.");
482
+ }
483
+ let stat;
484
+ try {
485
+ stat = fs.statSync(nodePath);
486
+ }
487
+ catch {
488
+ throw new Error(`ORY_PLUGIN_NODE_PATH does not exist: ${nodePath}`);
489
+ }
490
+ if (!stat.isFile()) {
491
+ throw new Error(`ORY_PLUGIN_NODE_PATH must be a regular file: ${nodePath}`);
492
+ }
493
+ try {
494
+ fs.accessSync(nodePath, fs.constants.X_OK);
495
+ }
496
+ catch {
497
+ throw new Error(`ORY_PLUGIN_NODE_PATH must be executable: ${nodePath}`);
498
+ }
499
+ return nodePath;
500
+ }
501
+ function quoteShellArgument(value) {
502
+ return `"${value.replace(/["\\$`]/g, "\\$&")}"`;
503
+ }
461
504
  /**
462
505
  * The hook command a harness config should carry: `node "<shim>"`.
463
506
  *
464
- * Quoted because a data dir can contain spaces, and several harnesses run the
465
- * command through a shell.
507
+ * Paths are shell-quoted because managed executables and data dirs can contain
508
+ * spaces or shell metacharacters, and several harnesses run this as a string.
466
509
  */
467
- function hookCommand(shimPath) {
468
- return `node "${shimPath}"`;
510
+ function hookCommand(shimPath, nodePath = resolvePluginNodePath()) {
511
+ const command = nodePath === "node" ? nodePath : quoteShellArgument(nodePath);
512
+ return `${command} ${quoteShellArgument(shimPath)}`;
469
513
  }
470
514
  /**
471
515
  * True when a command in a harness config is one of ours.
@@ -487,8 +531,8 @@ function isOryRuntimeCommand(command) {
487
531
  /\bory-[a-z0-9-]+-hook\b/.test(command));
488
532
  }
489
533
  /** The MCP server registration a harness config should carry. */
490
- function mcpCommand(shimPath) {
491
- return { command: "node", args: [shimPath] };
534
+ function mcpCommand(shimPath, nodePath = resolvePluginNodePath()) {
535
+ return { command: nodePath, args: [shimPath] };
492
536
  }
493
537
  /**
494
538
  * Module specifier for an *in-process* plugin: the absolute package directory.
@@ -602,8 +646,9 @@ function pruneRuntimeStores() {
602
646
  * `ORY_PLUGIN_RUNTIME_DIR` → link that directory
603
647
  * otherwise → materialize the CLI's own version
604
648
  *
605
- * The flag and the env var support explicit source installs that point at an
606
- * already-built workspace package instead of materializing from a registry.
649
+ * The flag and the env var support explicit prebuilt runtime installs,
650
+ * including administrator-managed package directories and workspace builds,
651
+ * instead of materializing from a registry.
607
652
  *
608
653
  * The flag deliberately requires an explicit directory. A bare "link whatever
609
654
  * package I am running from" would resolve to npx's ephemeral cache dir under
@@ -733,6 +778,9 @@ function wireRuntime(opts) {
733
778
  return withRuntimeInstallLock(() => wireRuntimeUnlocked(opts));
734
779
  }
735
780
  function wireRuntimeUnlocked(opts) {
781
+ // Validate before npm or filesystem wiring so a managed install fails
782
+ // without side effects when its configured executable is unusable.
783
+ const nodePath = resolvePluginNodePath();
736
784
  const target = resolveRuntimeForInstall({
737
785
  packageName: opts.packageName,
738
786
  packageRoot: opts.packageRoot,
@@ -761,7 +809,7 @@ function wireRuntimeUnlocked(opts) {
761
809
  fallbackStdout: opts.fallbackStdout,
762
810
  mcpEntry,
763
811
  });
764
- hookCmd = hookCommand(hookShim);
812
+ hookCmd = hookCommand(hookShim, nodePath);
765
813
  }
766
814
  const mcpShim = opts.includeMcp === false
767
815
  ? undefined
@@ -775,7 +823,7 @@ function wireRuntimeUnlocked(opts) {
775
823
  target,
776
824
  hookShim,
777
825
  ...(hookCmd ? { hookCommand: hookCmd } : {}),
778
- ...(mcpShim ? { mcpShim, mcpServer: mcpCommand(mcpShim) } : {}),
826
+ ...(mcpShim ? { mcpShim, mcpServer: mcpCommand(mcpShim, nodePath) } : {}),
779
827
  prunedStores: pruneRuntimeStores(),
780
828
  };
781
829
  }
package/dist/setup.js CHANGED
@@ -356,13 +356,13 @@ Options:
356
356
  Environment variables:
357
357
  ORY_PROJECT_URL Your Ory project URL (required at runtime)
358
358
  ORY_AGENT_SECURITY_URL Canonical Agent Security origin (legacy fallback: project URL)
359
- ORY_AGENT_CLIENT_ID Static agent OAuth2 client_credentials (with
360
- ORY_AGENT_CLIENT_SECRET …SECRET) — otherwise the agent self-registers
359
+ ORY_AGENT_API_KEY Explicit Talos runtime credential (otherwise session
360
+ start loads or enrolls one in the OS credential store)
361
361
  ORY_OAUTH2_CLIENT_ID Override the public OAuth2 client id for the user PKCE flow
362
362
  (default: ory-agent-security-login)
363
363
  ORY_AGENT_DEBUG Set to "true" for debug logging
364
364
  ORY_AGENT_LOG_FILE Path to write debug logs
365
- ORY_AGENT_SUBJECT_ID Override the subject ID for permission checks
365
+ ORY_AGENT_SUBJECT_ID Override the subject for injected/env credentials
366
366
  `);
367
367
  }
368
368
  let deferNextStepsActive = false;
package/dist/skills.d.ts CHANGED
@@ -3,11 +3,9 @@
3
3
  *
4
4
  * The skill playbooks (auth-setup, login-flow, social-login, local-dev,
5
5
  * permissions-onboarding, contribute-integration, build-integration,
6
- * e2b-sandbox, build-agent) and the local-stack commands (local-up, local-down) live once,
7
- * as token-bearing templates under `packages/core/assets/`. Every harness plugin renders them
8
- * e2b-sandbox, temporal-worker) and the local-stack commands (local-up,
9
- * local-down, temporal-up) live once, as token-bearing templates under
10
- * `packages/core/assets/`. Every harness plugin renders them
6
+ * e2b-sandbox, build-agent, deploy-agent, deploy-openai-e2b, temporal-worker) and local-stack
7
+ * commands (local-up, local-down, temporal-up) live once as token-bearing
8
+ * templates under `packages/core/assets/`. Every harness plugin renders them
11
9
  * through {@link renderOrySkills} / {@link renderOryCommands}, substituting the
12
10
  * harness's CLI binary, package name, and the way it references sibling skills
13
11
  * and commands. Plugins then write the rendered docs into whatever location
package/dist/skills.js CHANGED
@@ -4,11 +4,9 @@
4
4
  *
5
5
  * The skill playbooks (auth-setup, login-flow, social-login, local-dev,
6
6
  * permissions-onboarding, contribute-integration, build-integration,
7
- * e2b-sandbox, build-agent) and the local-stack commands (local-up, local-down) live once,
8
- * as token-bearing templates under `packages/core/assets/`. Every harness plugin renders them
9
- * e2b-sandbox, temporal-worker) and the local-stack commands (local-up,
10
- * local-down, temporal-up) live once, as token-bearing templates under
11
- * `packages/core/assets/`. Every harness plugin renders them
7
+ * e2b-sandbox, build-agent, deploy-agent, deploy-openai-e2b, temporal-worker) and local-stack
8
+ * commands (local-up, local-down, temporal-up) live once as token-bearing
9
+ * templates under `packages/core/assets/`. Every harness plugin renders them
12
10
  * through {@link renderOrySkills} / {@link renderOryCommands}, substituting the
13
11
  * harness's CLI binary, package name, and the way it references sibling skills
14
12
  * and commands. Plugins then write the rendered docs into whatever location
@@ -94,6 +92,16 @@ const SKILL_SOURCES = [
94
92
  name: "ory-build-agent",
95
93
  file: "skills/ory-build-agent/SKILL.md",
96
94
  },
95
+ {
96
+ id: "deploy-agent",
97
+ name: "ory-deploy-agent",
98
+ file: "skills/ory-deploy-agent/SKILL.md",
99
+ },
100
+ {
101
+ id: "deploy-openai-e2b",
102
+ name: "ory-deploy-openai-e2b",
103
+ file: "skills/ory-deploy-openai-e2b/SKILL.md",
104
+ },
97
105
  {
98
106
  id: "temporal-worker",
99
107
  name: "ory-temporal-worker",
@@ -181,6 +189,7 @@ function buildProfile(harness, opts) {
181
189
  "{{REF_SOCIAL_LOGIN}}": skillRef("ory-social-login"),
182
190
  "{{REF_LOCAL_DEV}}": skillRef("ory-local-dev"),
183
191
  "{{REF_PERMISSIONS_ONBOARDING}}": skillRef("ory-permissions-onboarding"),
192
+ "{{REF_DEPLOY_OPENAI_E2B}}": skillRef("ory-deploy-openai-e2b"),
184
193
  "{{REF_TEMPORAL_WORKER}}": skillRef("ory-temporal-worker"),
185
194
  "{{REF_LOCAL_UP}}": localUp,
186
195
  "{{REF_LOCAL_DOWN}}": localDown,
package/dist/subject.d.ts CHANGED
@@ -114,8 +114,7 @@ export declare function validateSubjectOverride(raw: string | undefined, envVar?
114
114
  */
115
115
  export declare function readAgentSubjectOverride(env?: NodeJS.ProcessEnv, warnings?: string[]): string | undefined;
116
116
  /**
117
- * The registered agent credential subject: `Agent:<clientId>`. For DCR this
118
- * client belongs to one session; static credentials may be shared explicitly.
117
+ * The actor-bound agent credential subject: `Agent:<rootActor>`.
119
118
  *
120
119
  * `undefined` when no agent principal is populated — the agent gate never
121
120
  * blocks, so a session with no resolved machine identity is normal and callers
@@ -123,7 +122,7 @@ export declare function readAgentSubjectOverride(env?: NodeJS.ProcessEnv, warnin
123
122
  */
124
123
  export declare function resolveAgentSubject(client: OryAgentClient): UserSubjectRef | undefined;
125
124
  /**
126
- * The **session-level** agent subject: `Agent:<clientId>|<session>`, unique to
125
+ * The **session-level** agent subject: `Agent:<rootActor>|<session>`, unique to
127
126
  * one run. Used for a block that stops the session's narrower subject without
128
127
  * blocking its base credential subject.
129
128
  *
package/dist/subject.js CHANGED
@@ -117,17 +117,17 @@ function subjectLabel(ref) {
117
117
  }
118
118
  // ─── Machine principals (agent / sub-agent) ──────────────────────────
119
119
  //
120
- // The agent's OAuth2 credential is session-scoped: one DCR client per
121
- // `(projectUrl, harness, session)`, plus one client per typed sub-agent in that
122
- // session. Sessionless integrations use a reserved persistent slot.
120
+ // Runtime credentials are descriptor-scoped: one root actor per
121
+ // `(projectUrl, harness, session)`, plus one child actor per stable sub-agent
122
+ // spawn in that session. Sessionless integrations use a reserved persistent slot.
123
123
  //
124
- // The registered client id is the principal root. Session and spawn axes remain
124
+ // The enrolled actor id is the principal root. Session and spawn axes remain
125
125
  // available as narrower broker subjects and are appended with `|`:
126
126
  //
127
- // Agent:<clientId> session credential
128
- // Agent:<clientId>|<session> one run
129
- // SubAgent:<clientId> typed sub-agent credential
130
- // SubAgent:<clientId>|<session>|<type>[|<spawn>] one spawn
127
+ // Agent:<rootActor> session credential
128
+ // Agent:<rootActor>|<session> one run
129
+ // SubAgent:<childActor> child credential
130
+ // SubAgent:<childActor>|<session>|<type>[|<spawn>] one spawn
131
131
  //
132
132
  // Every one of these is computable locally, so a block can be written in the
133
133
  // Console against the exact string the plugin will check. Nothing here depends
@@ -207,8 +207,7 @@ function readAgentSubjectOverride(env = process.env, warnings) {
207
207
  return value;
208
208
  }
209
209
  /**
210
- * The registered agent credential subject: `Agent:<clientId>`. For DCR this
211
- * client belongs to one session; static credentials may be shared explicitly.
210
+ * The actor-bound agent credential subject: `Agent:<rootActor>`.
212
211
  *
213
212
  * `undefined` when no agent principal is populated — the agent gate never
214
213
  * blocks, so a session with no resolved machine identity is normal and callers
@@ -219,7 +218,7 @@ function resolveAgentSubject(client) {
219
218
  return subject ? subjectSet(exports.AGENT_NAMESPACE, subject) : undefined;
220
219
  }
221
220
  /**
222
- * The **session-level** agent subject: `Agent:<clientId>|<session>`, unique to
221
+ * The **session-level** agent subject: `Agent:<rootActor>|<session>`, unique to
223
222
  * one run. Used for a block that stops the session's narrower subject without
224
223
  * blocking its base credential subject.
225
224
  *
package/dist/testing.d.ts CHANGED
@@ -10,6 +10,22 @@ import { OryAgentClient } from "./client.js";
10
10
  import type { ActivityEntry } from "./logger.js";
11
11
  import type { PermissionMode } from "./config.js";
12
12
  export { runHarnessContractSuite, type HarnessContractAdapter, type ContractContext, type ContractGates, type ContractOutcome, } from "./contract-suite.js";
13
+ export interface SdkLazySessionContractAdapter {
14
+ name: string;
15
+ harness: string;
16
+ createSessionStarterMock: ReturnType<typeof vi.fn>;
17
+ ensureSessionMock: ReturnType<typeof vi.fn>;
18
+ createSubject(client: OryAgentClient, projectUrl?: string): {
19
+ invoke(index: number): Promise<unknown>;
20
+ };
21
+ expectedConcurrentResults: unknown[];
22
+ expectedSingleResult: unknown;
23
+ }
24
+ /**
25
+ * Verify the common lazy-start contract used by TypeScript SDK integrations.
26
+ * The package adapter supplies only its native invocation shape.
27
+ */
28
+ export declare function runSdkLazySessionContract(adapter: SdkLazySessionContractAdapter): void;
13
29
  /**
14
30
  * Create an OryAgentClient with session caching disabled.
15
31
  * Pass overrides to customize (e.g. a different harness name).
@@ -30,6 +46,7 @@ export declare function spyRecordDelegation(client: OryAgentClient, result?: {
30
46
  agentToken?: string;
31
47
  runtimeCredential?: import("./runtime-credential.js").RuntimeCredential;
32
48
  signal?: AbortSignal;
49
+ preprovisionedRoot?: boolean;
33
50
  }) => Promise<import("./client.js").RecordDelegationResult>>;
34
51
  /**
35
52
  * Stub an internal API instance method on the client.
package/dist/testing.js CHANGED
@@ -41,6 +41,7 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  })();
42
42
  Object.defineProperty(exports, "__esModule", { value: true });
43
43
  exports.TEST_OAUTH2_CLIENT_ID = exports.TEST_AGENT_SECURITY_URL = exports.TEST_PROJECT_URL = exports.BATCH_SERVER_DENIED = exports.BATCH_SERVER_ALLOWED_TOOL_DENIED = exports.BATCH_BOTH_ALLOWED = exports.PERMISSION_DENIED = exports.PERMISSION_ALLOWED = exports.MOCK_INACTIVE_OAUTH2_RESPONSE = exports.MOCK_OAUTH2_RESPONSE = exports.MOCK_INACTIVE_SESSION_RESPONSE = exports.MOCK_SESSION_RESPONSE = exports.runHarnessContractSuite = void 0;
44
+ exports.runSdkLazySessionContract = runSdkLazySessionContract;
44
45
  exports.createMockClient = createMockClient;
45
46
  exports.spyRecordDelegation = spyRecordDelegation;
46
47
  exports.stubApi = stubApi;
@@ -85,6 +86,58 @@ const permission_mode_js_1 = require("./permission-mode.js");
85
86
  // adapter shape.
86
87
  var contract_suite_js_1 = require("./contract-suite.js");
87
88
  Object.defineProperty(exports, "runHarnessContractSuite", { enumerable: true, get: function () { return contract_suite_js_1.runHarnessContractSuite; } });
89
+ /**
90
+ * Verify the common lazy-start contract used by TypeScript SDK integrations.
91
+ * The package adapter supplies only its native invocation shape.
92
+ */
93
+ function runSdkLazySessionContract(adapter) {
94
+ let cleanup;
95
+ (0, vitest_1.beforeEach)(() => {
96
+ cleanup = setOryEnv();
97
+ adapter.createSessionStarterMock.mockClear();
98
+ adapter.ensureSessionMock.mockReset();
99
+ adapter.ensureSessionMock.mockResolvedValue(undefined);
100
+ });
101
+ (0, vitest_1.afterEach)(() => {
102
+ cleanup?.();
103
+ clearOryEnv();
104
+ });
105
+ (0, vitest_1.describe)(`${adapter.name} - lazy session startup`, () => {
106
+ (0, vitest_1.it)("awaits the shared session starter for concurrent first calls", async () => {
107
+ const client = createMockClient({ harness: adapter.harness });
108
+ client.setUserPrincipal({ subject: "user:alice" });
109
+ stubPermissionAllowed(client);
110
+ let release;
111
+ adapter.ensureSessionMock.mockReturnValue(new Promise((resolve) => {
112
+ release = resolve;
113
+ }));
114
+ const subject = adapter.createSubject(client);
115
+ let settled = 0;
116
+ const calls = [0, 1].map((index) => subject.invoke(index).then((result) => {
117
+ settled += 1;
118
+ return result;
119
+ }));
120
+ await new Promise((resolve) => setTimeout(resolve, 0));
121
+ (0, vitest_1.expect)(adapter.createSessionStarterMock).toHaveBeenCalledTimes(1);
122
+ (0, vitest_1.expect)(adapter.ensureSessionMock).toHaveBeenCalledTimes(2);
123
+ (0, vitest_1.expect)(settled).toBe(0);
124
+ release();
125
+ await (0, vitest_1.expect)(Promise.all(calls)).resolves.toEqual(adapter.expectedConcurrentResults);
126
+ (0, vitest_1.expect)(settled).toBe(2);
127
+ });
128
+ (0, vitest_1.it)("passes the SDK harness and project URL to the shared starter", async () => {
129
+ const client = createMockClient({ harness: adapter.harness });
130
+ client.setUserPrincipal({ subject: "user:alice" });
131
+ stubPermissionAllowed(client);
132
+ const subject = adapter.createSubject(client, "https://ory.test");
133
+ await (0, vitest_1.expect)(subject.invoke(0)).resolves.toEqual(adapter.expectedSingleResult);
134
+ (0, vitest_1.expect)(adapter.createSessionStarterMock).toHaveBeenCalledWith(client, {
135
+ harness: adapter.harness,
136
+ projectUrl: "https://ory.test",
137
+ });
138
+ });
139
+ });
140
+ }
88
141
  // ─── Client Helpers ────────────────────────────────────────────────
89
142
  /**
90
143
  * Create an OryAgentClient with session caching disabled.
@@ -14,19 +14,19 @@
14
14
  * Built-in tool names known to ship with each supported harness.
15
15
  */
16
16
  export declare const HARNESS_TOOL_CATALOG: {
17
- readonly "claude-code": readonly ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "WebFetch", "WebSearch", "NotebookEdit", "ToolSearch", "Skill", "TodoWrite", "Agent", "Task"];
18
- readonly "claude-agent-sdk": readonly ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "WebFetch", "WebSearch", "NotebookEdit", "ToolSearch", "Skill", "TodoWrite", "Agent", "Task"];
19
- readonly codex: readonly ["Bash", "apply_patch"];
20
- readonly "gemini-cli": readonly ["read_file", "write_file", "edit_file", "list_files", "search_files", "shell", "web_search"];
21
- readonly openclaw: readonly ["exec", "read", "write", "edit", "apply_patch", "web_search", "web_fetch", "browser"];
22
- readonly opencode: readonly ["read", "write", "edit", "bash", "glob", "grep", "webfetch"];
23
- readonly continue: readonly ["Bash", "Read", "Edit", "Write", "Grep", "Glob", "WebSearch"];
24
- readonly goose: readonly ["shell", "text_editor", "read_file", "write_file", "list_windows", "screen_capture"];
25
- readonly cline: readonly ["execute_command", "read_file", "write_to_file", "replace_in_file", "search_files", "list_files", "use_mcp_tool"];
26
- readonly amp: readonly ["Bash", "Read", "create_file", "edit_file", "undo_edit", "glob", "Grep", "finder", "read_web_page", "web_search", "todo_read", "todo_write", "oracle", "Task"];
17
+ readonly "claude-code": readonly ["Read", "Write", "Edit", "Bash", "Monitor", "PowerShell", "Workflow", "Glob", "Grep", "WebFetch", "WebSearch", "NotebookEdit", "ToolSearch", "Skill", "TodoWrite", "Agent", "Task", "Artifact", "CronCreate", "CronDelete", "CronList", "EndConversation", "EnterPlanMode", "EnterWorktree", "ExitWorktree", "ListAgents", "ListMcpResourcesTool", "LSP", "PushNotification", "ReadMcpResourceTool", "RemoteTrigger", "ReportFindings", "ScheduleWakeup", "SendFeedback", "SendMessage", "SendUserFile", "ShareOnboardingGuide", "SubagentHandback", "TaskCreate", "TaskGet", "TaskList", "TaskOutput", "TaskStop", "TaskUpdate", "WaitForMcpServers"];
18
+ readonly "claude-agent-sdk": readonly ["Read", "Write", "Edit", "Bash", "Monitor", "PowerShell", "Workflow", "Glob", "Grep", "WebFetch", "WebSearch", "NotebookEdit", "ToolSearch", "Skill", "TodoWrite", "Agent", "Task", "Artifact", "CronCreate", "CronDelete", "CronList", "EndConversation", "EnterPlanMode", "EnterWorktree", "ExitWorktree", "ListAgents", "ListMcpResourcesTool", "LSP", "PushNotification", "ReadMcpResourceTool", "RemoteTrigger", "ReportFindings", "ScheduleWakeup", "SendFeedback", "SendMessage", "SendUserFile", "ShareOnboardingGuide", "SubagentHandback", "TaskCreate", "TaskGet", "TaskList", "TaskOutput", "TaskStop", "TaskUpdate", "WaitForMcpServers"];
19
+ readonly codex: readonly ["Bash", "apply_patch", "webrun", "spawn_agent"];
20
+ readonly "gemini-cli": readonly ["run_shell_command", "shell", "glob", "grep_search", "list_directory", "read_file", "read_many_files", "replace", "write_file", "write_todos", "tracker_create_task", "tracker_update_task", "tracker_get_task", "tracker_list_tasks", "tracker_add_dependency", "tracker_visualize", "list_mcp_resources", "read_mcp_resource", "activate_skill", "get_internal_docs", "enter_plan_mode", "update_topic", "complete_task", "invoke_agent", "google_web_search", "web_fetch"];
21
+ readonly openclaw: readonly ["exec", "read", "write", "edit", "apply_patch", "web_search", "web_fetch", "browser", "process", "code_execution", "x_search", "secrets", "message", "gateway", "plugins", "nodes", "computer", "screen", "theme", "dashboard", "terminal", "portal", "canvas", "show_widget", "sessions", "sessions_list", "sessions_history", "sessions_search", "sessions_send", "sessions_spawn", "sessions_yield", "subagents", "session_status", "memory_search", "memory_get", "agents_list", "get_goal", "create_goal", "update_goal", "progress_card", "view_image", "image_generate", "music_generate", "video_generate", "tts", "pdf"];
22
+ readonly opencode: readonly ["read", "write", "edit", "bash", "glob", "grep", "webfetch", "apply_patch", "skill", "todowrite", "websearch", "task", "lsp", "execute"];
23
+ readonly continue: readonly ["Edit", "Exit", "Fetch", "List", "MultiEdit", "Read", "ReportFailure", "Bash", "Search", "Status", "Subagent", "Skills", "UploadArtifact", "Diff", "Checklist", "Write"];
24
+ readonly goose: readonly ["analyze", "write", "edit", "shell", "tree", "read_image", "todo__todo_write", "apps__list_apps", "apps__create_app", "apps__iterate_app", "apps__delete_app", "extensionmanager__search_available_extensions", "extensionmanager__manage_extensions", "extensionmanager__list_resources", "extensionmanager__read_resource", "load", "delegate", "load_skill"];
25
+ readonly cline: readonly ["read_files", "search_codebase", "run_commands", "fetch_web_content", "apply_patch", "editor", "skills", "execute_command", "read_file", "write_to_file", "replace_in_file", "search_files", "list_files", "use_mcp_tool", "browser_action"];
26
+ readonly amp: readonly ["Bash", "Read", "create_file", "edit_file", "undo_edit", "glob", "Grep", "finder", "read_web_page", "web_search", "todo_read", "todo_write", "oracle", "Task", "create_thread", "list_agent_modes", "get_thread_status", "send_thread_message", "wait_for_threads"];
27
27
  readonly pi: readonly ["read", "write", "edit", "bash", "grep", "find", "ls"];
28
28
  readonly antigravity: readonly ["run_command", "view_file", "write_to_file", "replace_file_content", "multi_replace_file_content", "list_directory", "grep_search", "find", "read_url_content", "search_web", "call_mcp_tool"];
29
- readonly cursor: readonly ["Read", "Write", "Edit", "Delete", "Shell", "List", "Search", "MCP", "Task"];
29
+ readonly cursor: readonly ["Read", "Write", "Grep", "Delete", "Shell", "Task"];
30
30
  };
31
31
  /** Names of the harnesses with a known tool catalog. */
32
32
  export type KnownHarness = keyof typeof HARNESS_TOOL_CATALOG;
@@ -109,10 +109,12 @@ export declare function getShellCommandCatalog(): readonly string[];
109
109
  */
110
110
  export declare function resolveShellCommandNamespace(): string;
111
111
  /**
112
- * Extract the shell command string from a harness's tool-args object — the same
112
+ * Extract shell command strings from a harness's tool-args object — the same
113
113
  * object the handler already passes to `summarizeToolInput`. Returns `undefined`
114
114
  * when the tool isn't a shell tool for this harness or no command field is
115
- * present (the gate then skips decomposition and degrades to the top-level
116
- * `Bash` check — it never breaks).
115
+ * present. Cline's `run_commands` is the exception: malformed command data is
116
+ * represented as an unresolvable command so enforce mode fails closed. Cline's
117
+ * command array remains an array so independently executed entries cannot
118
+ * change how neighboring entries parse.
117
119
  */
118
- export declare function extractShellCommand(harness: string, toolName: string, args: unknown): string | undefined;
120
+ export declare function extractShellCommand(harness: string, toolName: string, args: unknown): string | readonly string[] | undefined;