@ory/argus 0.8.1 → 0.8.2
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/dist/index.d.ts +3 -2
- package/dist/index.js +12 -1
- package/dist/lifecycle.d.ts +59 -0
- package/dist/lifecycle.js +123 -0
- package/dist/permissions.d.ts +51 -0
- package/dist/permissions.js +48 -0
- package/dist/tool-catalog.d.ts +31 -0
- package/dist/tool-catalog.js +58 -1
- package/dist/tracer.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -15,8 +15,9 @@ export { runDevLauncher, type DevLauncherConfig, type InstallContext, } from "./
|
|
|
15
15
|
export { renderOrySkills, renderOryCommands, commandToSkill, commandToToml, commandToFrontmatterMarkdown, commandToPlainMarkdown, toSkillMarkdown, writeSkillTree, removeSkillDirs, ORY_SKILL_NAMES, ORY_COMMAND_SKILL_NAMES, ORY_COMMAND_SLUGS, type RenderedSkill, type RenderedCommand, type RenderProfileOptions, } from "./skills.js";
|
|
16
16
|
export { runLocalCommand, ensureDevJaeger, stopDevJaeger, DEV_JAEGER_CONTAINER, type EnsureDevJaegerResult, type StopDevJaegerResult, } from "./local/index.js";
|
|
17
17
|
export { runRegistryCommand } from "./registry/index.js";
|
|
18
|
-
export { checkAndDecide, applyPermissionMode, type PermissionDecision, type ModeDecision, type DecisionSpanAttributes, type CheckAndDecideOptions, type ApplyPermissionModeContext, } from "./permissions.js";
|
|
19
|
-
export { HARNESS_TOOL_CATALOG, KNOWN_HARNESSES, ALL_TOOLS, getToolCatalog, type KnownHarness, } from "./tool-catalog.js";
|
|
18
|
+
export { checkAndDecide, applyPermissionMode, gateToolCall, type PermissionDecision, type ModeDecision, type DecisionSpanAttributes, type CheckAndDecideOptions, type ApplyPermissionModeContext, type GateToolCallArgs, type ToolGateOutcome, } from "./permissions.js";
|
|
19
|
+
export { HARNESS_TOOL_CATALOG, KNOWN_HARNESSES, ALL_TOOLS, getToolCatalog, INTERACTIVE_TOOL_CATALOG, getInteractiveToolCatalog, isInteractiveTool, type KnownHarness, } from "./tool-catalog.js";
|
|
20
|
+
export { classifyLifecycle, isUserFacingPhase, isToolExecutionPhase, HARNESS_LIFECYCLE_MAP, USER_FACING_PHASES, TOOL_EXECUTION_PHASES, type LifecyclePhase, } from "./lifecycle.js";
|
|
20
21
|
export { parseClaudeCodeMcpTool, parseGeminiMcpTool, parseMcpToolGeneric, checkMcpPermission, type McpToolIdentifier, type McpPermissionCheckOptions, type McpPermissionResult, } from "./mcp.js";
|
|
21
22
|
export { resolveUserSubject, subjectLabel, type UserSubjectRef, } from "./subject.js";
|
|
22
23
|
export { formatDenialMessage, formatDenialSummary, formatAlertMessage, formatAlertSummary, alertAttributes, OryDenialError, type DenialContext, type AlertAttributes, } from "./denial.js";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.printOryConfig = exports.runAgentCommand = exports.runConfigureCommand = exports.AGENT_TOKEN_EXPIRY_SKEW_SEC = exports.clearSubAgentDynamicCredentials = exports.saveSubAgentDynamicCredentials = exports.loadSubAgentDynamicCredentials = exports.clearAgentDynamicCredentials = exports.saveAgentDynamicCredentials = exports.loadAgentDynamicCredentials = exports.registerAgentClient = exports.fetchClientCredentialsToken = exports.ensureSubAgentIdentity = exports.ensureAgentIdentity = exports.resolveAgentCredentials = exports.ensureAuthenticated = exports.ensureUserAuthenticated = exports.TOKEN_EXPIRY_SKEW_SEC = exports.waitForPeerTokensSync = exports.waitForPeerTokens = exports.clearPkceFlightLock = exports.tryAcquirePkceFlightLock = exports.refreshAndSave = exports.isExpired = exports.clearTokens = exports.saveTokens = exports.loadTokens = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.LOOPBACK_PORTS = exports.buildAuthorizeUrl = exports.sha256Base64Url = exports.generateCodeVerifier = exports.detectHeadless = exports.refreshAccessToken = exports.pkceLogin = exports.getHarnessDataDir = exports.getDataDir = exports.getConfigPath = exports.mutateConfig = exports.resolveConfig = exports.saveConfig = exports.loadConfig = exports.watchTraceFile = exports.formatSpan = exports.deriveTraceId = exports.ActiveSpan = exports.Tracer = exports.redactLogData = exports.DebugLogger = exports.OryAgentClient = void 0;
|
|
4
4
|
exports.runLocalCommand = exports.ORY_COMMAND_SLUGS = exports.ORY_COMMAND_SKILL_NAMES = exports.ORY_SKILL_NAMES = exports.removeSkillDirs = exports.writeSkillTree = exports.toSkillMarkdown = exports.commandToPlainMarkdown = exports.commandToFrontmatterMarkdown = exports.commandToToml = exports.commandToSkill = exports.renderOryCommands = exports.renderOrySkills = exports.runDevLauncher = exports.unregisterPlugin = exports.registerPlugin = exports.removeMcpServer = exports.mergeMcpServer = exports.mcpServerEntry = exports.resolveMcpServerCommand = exports.printNextSteps = exports.printSetupHelp = exports.removeFlatHooks = exports.mergeFlatHooks = exports.flatHookEntry = exports.removeMatcherHooks = exports.mergeMatcherHooks = exports.matcherHookEntry = exports.resolveHookCommand = exports.isOryHookCommand = exports.writeJsonFile = exports.readJsonFile = exports.parseSetupArgs = exports.printPermissionsSection = exports.printAgentIdentitySection = exports.printUserIdentitySection = exports.runStatusCommand = exports.printPermissionsOnboardingHelp = exports.maybeAutoBootstrap = exports.isUserIdentityCached = exports.runPermissionsCommand = exports.interactiveConfigPrompt = exports.promptForProjectUrl = exports.promptOnTty = exports.isTtyAvailable = exports.runWatchCommand = exports.printTraceTail = exports.printEnvHelp = exports.printLogTail = exports.printEnvironment = void 0;
|
|
5
|
-
exports.parseKeyValueList = exports.otlpExporterFromEnv = exports.OtlpExporter = exports.summarizeToolOutput = exports.summarizeToolInput = exports.OryDenialError = exports.alertAttributes = exports.formatAlertSummary = exports.formatAlertMessage = exports.formatDenialSummary = exports.formatDenialMessage = exports.subjectLabel = exports.resolveUserSubject = exports.checkMcpPermission = exports.parseMcpToolGeneric = exports.parseGeminiMcpTool = exports.parseClaudeCodeMcpTool = exports.getToolCatalog = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = exports.applyPermissionMode = exports.checkAndDecide = exports.runRegistryCommand = exports.DEV_JAEGER_CONTAINER = exports.stopDevJaeger = exports.ensureDevJaeger = void 0;
|
|
5
|
+
exports.parseKeyValueList = exports.otlpExporterFromEnv = exports.OtlpExporter = exports.summarizeToolOutput = exports.summarizeToolInput = exports.OryDenialError = exports.alertAttributes = exports.formatAlertSummary = exports.formatAlertMessage = exports.formatDenialSummary = exports.formatDenialMessage = exports.subjectLabel = exports.resolveUserSubject = exports.checkMcpPermission = exports.parseMcpToolGeneric = exports.parseGeminiMcpTool = exports.parseClaudeCodeMcpTool = exports.TOOL_EXECUTION_PHASES = exports.USER_FACING_PHASES = exports.HARNESS_LIFECYCLE_MAP = exports.isToolExecutionPhase = exports.isUserFacingPhase = exports.classifyLifecycle = exports.isInteractiveTool = exports.getInteractiveToolCatalog = exports.INTERACTIVE_TOOL_CATALOG = exports.getToolCatalog = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = exports.gateToolCall = exports.applyPermissionMode = exports.checkAndDecide = exports.runRegistryCommand = exports.DEV_JAEGER_CONTAINER = exports.stopDevJaeger = exports.ensureDevJaeger = void 0;
|
|
6
6
|
var client_js_1 = require("./client.js");
|
|
7
7
|
Object.defineProperty(exports, "OryAgentClient", { enumerable: true, get: function () { return client_js_1.OryAgentClient; } });
|
|
8
8
|
var logger_js_1 = require("./logger.js");
|
|
@@ -126,11 +126,22 @@ Object.defineProperty(exports, "runRegistryCommand", { enumerable: true, get: fu
|
|
|
126
126
|
var permissions_js_1 = require("./permissions.js");
|
|
127
127
|
Object.defineProperty(exports, "checkAndDecide", { enumerable: true, get: function () { return permissions_js_1.checkAndDecide; } });
|
|
128
128
|
Object.defineProperty(exports, "applyPermissionMode", { enumerable: true, get: function () { return permissions_js_1.applyPermissionMode; } });
|
|
129
|
+
Object.defineProperty(exports, "gateToolCall", { enumerable: true, get: function () { return permissions_js_1.gateToolCall; } });
|
|
129
130
|
var tool_catalog_js_1 = require("./tool-catalog.js");
|
|
130
131
|
Object.defineProperty(exports, "HARNESS_TOOL_CATALOG", { enumerable: true, get: function () { return tool_catalog_js_1.HARNESS_TOOL_CATALOG; } });
|
|
131
132
|
Object.defineProperty(exports, "KNOWN_HARNESSES", { enumerable: true, get: function () { return tool_catalog_js_1.KNOWN_HARNESSES; } });
|
|
132
133
|
Object.defineProperty(exports, "ALL_TOOLS", { enumerable: true, get: function () { return tool_catalog_js_1.ALL_TOOLS; } });
|
|
133
134
|
Object.defineProperty(exports, "getToolCatalog", { enumerable: true, get: function () { return tool_catalog_js_1.getToolCatalog; } });
|
|
135
|
+
Object.defineProperty(exports, "INTERACTIVE_TOOL_CATALOG", { enumerable: true, get: function () { return tool_catalog_js_1.INTERACTIVE_TOOL_CATALOG; } });
|
|
136
|
+
Object.defineProperty(exports, "getInteractiveToolCatalog", { enumerable: true, get: function () { return tool_catalog_js_1.getInteractiveToolCatalog; } });
|
|
137
|
+
Object.defineProperty(exports, "isInteractiveTool", { enumerable: true, get: function () { return tool_catalog_js_1.isInteractiveTool; } });
|
|
138
|
+
var lifecycle_js_1 = require("./lifecycle.js");
|
|
139
|
+
Object.defineProperty(exports, "classifyLifecycle", { enumerable: true, get: function () { return lifecycle_js_1.classifyLifecycle; } });
|
|
140
|
+
Object.defineProperty(exports, "isUserFacingPhase", { enumerable: true, get: function () { return lifecycle_js_1.isUserFacingPhase; } });
|
|
141
|
+
Object.defineProperty(exports, "isToolExecutionPhase", { enumerable: true, get: function () { return lifecycle_js_1.isToolExecutionPhase; } });
|
|
142
|
+
Object.defineProperty(exports, "HARNESS_LIFECYCLE_MAP", { enumerable: true, get: function () { return lifecycle_js_1.HARNESS_LIFECYCLE_MAP; } });
|
|
143
|
+
Object.defineProperty(exports, "USER_FACING_PHASES", { enumerable: true, get: function () { return lifecycle_js_1.USER_FACING_PHASES; } });
|
|
144
|
+
Object.defineProperty(exports, "TOOL_EXECUTION_PHASES", { enumerable: true, get: function () { return lifecycle_js_1.TOOL_EXECUTION_PHASES; } });
|
|
134
145
|
var mcp_js_1 = require("./mcp.js");
|
|
135
146
|
Object.defineProperty(exports, "parseClaudeCodeMcpTool", { enumerable: true, get: function () { return mcp_js_1.parseClaudeCodeMcpTool; } });
|
|
136
147
|
Object.defineProperty(exports, "parseGeminiMcpTool", { enumerable: true, get: function () { return mcp_js_1.parseGeminiMcpTool; } });
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical lifecycle vocabulary for harness plugins.
|
|
3
|
+
*
|
|
4
|
+
* Each harness exposes a different set of hook event names (Claude Code's
|
|
5
|
+
* `PreToolUse`, Gemini's `BeforeTool`, OpenCode's `tool.execute.before`,
|
|
6
|
+
* …). Mapping them onto a shared phase set lets the plugin code reason
|
|
7
|
+
* about *what is happening* without first remembering *which harness
|
|
8
|
+
* we're in*. Spans, logs, docs, and tests all reference the same set of
|
|
9
|
+
* phases.
|
|
10
|
+
*
|
|
11
|
+
* Phases split into three buckets:
|
|
12
|
+
*
|
|
13
|
+
* - **Session / agent lifecycle.** `session.start`, `session.stop`,
|
|
14
|
+
* `compaction`. The session begins or ends, or the harness compacts
|
|
15
|
+
* context. Plugins authenticate here (start) and may flush state
|
|
16
|
+
* (stop).
|
|
17
|
+
*
|
|
18
|
+
* - **User-facing interactions.** `user.prompt`, `user.interaction`,
|
|
19
|
+
* `permission.ask`. The harness is communicating with the human:
|
|
20
|
+
* they typed a prompt, the harness is surfacing a confirmation,
|
|
21
|
+
* the harness is asking whether to allow a tool. These are *traced*
|
|
22
|
+
* but never gated by a tool permission check — the user is the
|
|
23
|
+
* final decision-maker. (`permission.ask` does consult Ory to
|
|
24
|
+
* inform the harness's UI, but the call is advisory.)
|
|
25
|
+
*
|
|
26
|
+
* - **Tool execution.** `tool.before`, `tool.after`, `tool.failure`,
|
|
27
|
+
* `subagent.start`, `subagent.stop`. The agent is reaching into an
|
|
28
|
+
* external system (or spawning a child agent that will). These are
|
|
29
|
+
* the only phases that run a true `use` permission gate.
|
|
30
|
+
*
|
|
31
|
+
* `passthrough` is the explicit "nothing to do here" bucket — the
|
|
32
|
+
* harness fired an event we don't model, and we record an audit span
|
|
33
|
+
* and return.
|
|
34
|
+
*/
|
|
35
|
+
export type LifecyclePhase = "session.start" | "session.stop" | "user.prompt" | "user.interaction" | "permission.ask" | "tool.before" | "tool.after" | "tool.failure" | "subagent.start" | "subagent.stop" | "compaction" | "passthrough";
|
|
36
|
+
/**
|
|
37
|
+
* Per-harness map from the harness's native event name to a canonical
|
|
38
|
+
* {@link LifecyclePhase}. Event names that don't appear here resolve to
|
|
39
|
+
* `passthrough` via {@link classifyLifecycle}.
|
|
40
|
+
*/
|
|
41
|
+
export declare const HARNESS_LIFECYCLE_MAP: Record<string, Record<string, LifecyclePhase>>;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a harness event name to its canonical {@link LifecyclePhase}.
|
|
44
|
+
* Unknown harnesses or unknown event names both yield `passthrough`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function classifyLifecycle(harness: string, event: string): LifecyclePhase;
|
|
47
|
+
/**
|
|
48
|
+
* Phases that surface to the human (prompts, confirmations, notifications,
|
|
49
|
+
* advisory permission asks). Tool-call permission gates must never fire
|
|
50
|
+
* on these — the user is the decision-maker.
|
|
51
|
+
*/
|
|
52
|
+
export declare const USER_FACING_PHASES: ReadonlySet<LifecyclePhase>;
|
|
53
|
+
/**
|
|
54
|
+
* Phases that represent actual tool execution against external systems.
|
|
55
|
+
* Only these run a real `use` permission check.
|
|
56
|
+
*/
|
|
57
|
+
export declare const TOOL_EXECUTION_PHASES: ReadonlySet<LifecyclePhase>;
|
|
58
|
+
export declare function isUserFacingPhase(phase: LifecyclePhase): boolean;
|
|
59
|
+
export declare function isToolExecutionPhase(phase: LifecyclePhase): boolean;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Canonical lifecycle vocabulary for harness plugins.
|
|
4
|
+
*
|
|
5
|
+
* Each harness exposes a different set of hook event names (Claude Code's
|
|
6
|
+
* `PreToolUse`, Gemini's `BeforeTool`, OpenCode's `tool.execute.before`,
|
|
7
|
+
* …). Mapping them onto a shared phase set lets the plugin code reason
|
|
8
|
+
* about *what is happening* without first remembering *which harness
|
|
9
|
+
* we're in*. Spans, logs, docs, and tests all reference the same set of
|
|
10
|
+
* phases.
|
|
11
|
+
*
|
|
12
|
+
* Phases split into three buckets:
|
|
13
|
+
*
|
|
14
|
+
* - **Session / agent lifecycle.** `session.start`, `session.stop`,
|
|
15
|
+
* `compaction`. The session begins or ends, or the harness compacts
|
|
16
|
+
* context. Plugins authenticate here (start) and may flush state
|
|
17
|
+
* (stop).
|
|
18
|
+
*
|
|
19
|
+
* - **User-facing interactions.** `user.prompt`, `user.interaction`,
|
|
20
|
+
* `permission.ask`. The harness is communicating with the human:
|
|
21
|
+
* they typed a prompt, the harness is surfacing a confirmation,
|
|
22
|
+
* the harness is asking whether to allow a tool. These are *traced*
|
|
23
|
+
* but never gated by a tool permission check — the user is the
|
|
24
|
+
* final decision-maker. (`permission.ask` does consult Ory to
|
|
25
|
+
* inform the harness's UI, but the call is advisory.)
|
|
26
|
+
*
|
|
27
|
+
* - **Tool execution.** `tool.before`, `tool.after`, `tool.failure`,
|
|
28
|
+
* `subagent.start`, `subagent.stop`. The agent is reaching into an
|
|
29
|
+
* external system (or spawning a child agent that will). These are
|
|
30
|
+
* the only phases that run a true `use` permission gate.
|
|
31
|
+
*
|
|
32
|
+
* `passthrough` is the explicit "nothing to do here" bucket — the
|
|
33
|
+
* harness fired an event we don't model, and we record an audit span
|
|
34
|
+
* and return.
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.TOOL_EXECUTION_PHASES = exports.USER_FACING_PHASES = exports.HARNESS_LIFECYCLE_MAP = void 0;
|
|
38
|
+
exports.classifyLifecycle = classifyLifecycle;
|
|
39
|
+
exports.isUserFacingPhase = isUserFacingPhase;
|
|
40
|
+
exports.isToolExecutionPhase = isToolExecutionPhase;
|
|
41
|
+
/**
|
|
42
|
+
* Per-harness map from the harness's native event name to a canonical
|
|
43
|
+
* {@link LifecyclePhase}. Event names that don't appear here resolve to
|
|
44
|
+
* `passthrough` via {@link classifyLifecycle}.
|
|
45
|
+
*/
|
|
46
|
+
exports.HARNESS_LIFECYCLE_MAP = {
|
|
47
|
+
"claude-code": {
|
|
48
|
+
SessionStart: "session.start",
|
|
49
|
+
SessionEnd: "session.stop",
|
|
50
|
+
PreToolUse: "tool.before",
|
|
51
|
+
PostToolUse: "tool.after",
|
|
52
|
+
PostToolUseFailure: "tool.failure",
|
|
53
|
+
PermissionRequest: "permission.ask",
|
|
54
|
+
UserPromptSubmit: "user.prompt",
|
|
55
|
+
SubagentStart: "subagent.start",
|
|
56
|
+
SubagentStop: "subagent.stop",
|
|
57
|
+
},
|
|
58
|
+
codex: {
|
|
59
|
+
SessionStart: "session.start",
|
|
60
|
+
Stop: "session.stop",
|
|
61
|
+
PreToolUse: "tool.before",
|
|
62
|
+
PostToolUse: "tool.after",
|
|
63
|
+
PermissionRequest: "permission.ask",
|
|
64
|
+
UserPromptSubmit: "user.prompt",
|
|
65
|
+
},
|
|
66
|
+
"gemini-cli": {
|
|
67
|
+
SessionStart: "session.start",
|
|
68
|
+
SessionEnd: "session.stop",
|
|
69
|
+
BeforeTool: "tool.before",
|
|
70
|
+
AfterTool: "tool.after",
|
|
71
|
+
BeforeToolSelection: "tool.before",
|
|
72
|
+
Notification: "user.interaction",
|
|
73
|
+
PreCompress: "compaction",
|
|
74
|
+
},
|
|
75
|
+
openclaw: {
|
|
76
|
+
session_start: "session.start",
|
|
77
|
+
before_agent_run: "user.prompt",
|
|
78
|
+
before_tool_call: "tool.before",
|
|
79
|
+
after_tool_call: "tool.after",
|
|
80
|
+
subagent_spawning: "subagent.start",
|
|
81
|
+
subagent_spawned: "subagent.start",
|
|
82
|
+
subagent_ended: "subagent.stop",
|
|
83
|
+
},
|
|
84
|
+
opencode: {
|
|
85
|
+
config: "session.start",
|
|
86
|
+
"chat.message": "user.prompt",
|
|
87
|
+
"permission.ask": "permission.ask",
|
|
88
|
+
"tool.execute.before": "tool.before",
|
|
89
|
+
"tool.execute.after": "tool.after",
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Resolve a harness event name to its canonical {@link LifecyclePhase}.
|
|
94
|
+
* Unknown harnesses or unknown event names both yield `passthrough`.
|
|
95
|
+
*/
|
|
96
|
+
function classifyLifecycle(harness, event) {
|
|
97
|
+
return exports.HARNESS_LIFECYCLE_MAP[harness]?.[event] ?? "passthrough";
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Phases that surface to the human (prompts, confirmations, notifications,
|
|
101
|
+
* advisory permission asks). Tool-call permission gates must never fire
|
|
102
|
+
* on these — the user is the decision-maker.
|
|
103
|
+
*/
|
|
104
|
+
exports.USER_FACING_PHASES = new Set([
|
|
105
|
+
"user.prompt",
|
|
106
|
+
"user.interaction",
|
|
107
|
+
"permission.ask",
|
|
108
|
+
]);
|
|
109
|
+
/**
|
|
110
|
+
* Phases that represent actual tool execution against external systems.
|
|
111
|
+
* Only these run a real `use` permission check.
|
|
112
|
+
*/
|
|
113
|
+
exports.TOOL_EXECUTION_PHASES = new Set([
|
|
114
|
+
"tool.before",
|
|
115
|
+
"tool.after",
|
|
116
|
+
"tool.failure",
|
|
117
|
+
]);
|
|
118
|
+
function isUserFacingPhase(phase) {
|
|
119
|
+
return exports.USER_FACING_PHASES.has(phase);
|
|
120
|
+
}
|
|
121
|
+
function isToolExecutionPhase(phase) {
|
|
122
|
+
return exports.TOOL_EXECUTION_PHASES.has(phase);
|
|
123
|
+
}
|
package/dist/permissions.d.ts
CHANGED
|
@@ -117,6 +117,57 @@ export interface ApplyPermissionModeContext {
|
|
|
117
117
|
* proceed despite the deny.
|
|
118
118
|
*/
|
|
119
119
|
export declare function applyPermissionMode(client: OryAgentClient, allowed: boolean, context?: ApplyPermissionModeContext): ModeDecision;
|
|
120
|
+
/**
|
|
121
|
+
* Outcome of {@link gateToolCall}. Either the tool is a user-interaction
|
|
122
|
+
* primitive (`AskUserQuestion`, `ExitPlanMode`, `TodoWrite`, …) and we
|
|
123
|
+
* pass through with a single audit span, or it's a real tool execution
|
|
124
|
+
* and the caller gets the standard {@link PermissionDecision}.
|
|
125
|
+
*/
|
|
126
|
+
export type ToolGateOutcome = {
|
|
127
|
+
kind: "interactive";
|
|
128
|
+
/** Attributes to attach to the caller's pass-through trace, if any. */
|
|
129
|
+
spanAttributes: {
|
|
130
|
+
interactive: true;
|
|
131
|
+
toolName: string;
|
|
132
|
+
};
|
|
133
|
+
} | PermissionDecision;
|
|
134
|
+
export interface GateToolCallArgs {
|
|
135
|
+
/** Harness name (`claude-code`, `codex`, …). Used to look up the interactive-tool catalog. */
|
|
136
|
+
harness: string;
|
|
137
|
+
/** The tool the agent is invoking. */
|
|
138
|
+
toolName: string;
|
|
139
|
+
/** Permission check to run when the tool is a real execution. */
|
|
140
|
+
check: PermissionCheck;
|
|
141
|
+
/**
|
|
142
|
+
* Attributes merged into the permission span (real path) and the
|
|
143
|
+
* `user.interaction` span (interactive path).
|
|
144
|
+
*/
|
|
145
|
+
spanAttributes?: Record<string, unknown>;
|
|
146
|
+
/** Override the resolved {@link PermissionMode}. Tests use this. */
|
|
147
|
+
modeOverride?: PermissionMode;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Single entry point for the pre-tool-use gate. Splits the harness's
|
|
151
|
+
* incoming "tool" into two semantic categories:
|
|
152
|
+
*
|
|
153
|
+
* - **Interactive** — the tool surfaces UI to the user
|
|
154
|
+
* (`AskUserQuestion`, `ExitPlanMode`, `TodoWrite`, plus anything
|
|
155
|
+
* listed in `ORY_INTERACTIVE_TOOLS`). The plugin must not gate these
|
|
156
|
+
* through Ory: the user is the decision-maker, and blocking them in
|
|
157
|
+
* enforce mode (or logging a misleading observe-deny) hides the very
|
|
158
|
+
* prompt the user needs to see. We record one `user.interaction`
|
|
159
|
+
* audit span and return — no permission check, no `tool.invoke`,
|
|
160
|
+
* no `tool.block`.
|
|
161
|
+
*
|
|
162
|
+
* - **Execution** — every other tool. Delegates to
|
|
163
|
+
* {@link checkAndDecide} so observe/enforce/fail-open behavior is
|
|
164
|
+
* identical to the legacy code path.
|
|
165
|
+
*
|
|
166
|
+
* The caller branches on `outcome.kind`. The four execution kinds
|
|
167
|
+
* (`allow`, `deny`, `observe`, `fail_open`) keep their existing
|
|
168
|
+
* semantics; the new `interactive` kind means "do nothing else."
|
|
169
|
+
*/
|
|
170
|
+
export declare function gateToolCall(client: OryAgentClient, args: GateToolCallArgs): Promise<ToolGateOutcome>;
|
|
120
171
|
/**
|
|
121
172
|
* Run a permission check and resolve the configured mode against the
|
|
122
173
|
* result. Never throws — fail-open scenarios are surfaced as a typed
|
package/dist/permissions.js
CHANGED
|
@@ -23,8 +23,10 @@
|
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.applyPermissionMode = applyPermissionMode;
|
|
26
|
+
exports.gateToolCall = gateToolCall;
|
|
26
27
|
exports.checkAndDecide = checkAndDecide;
|
|
27
28
|
const config_js_1 = require("./config.js");
|
|
29
|
+
const tool_catalog_js_1 = require("./tool-catalog.js");
|
|
28
30
|
function formatSubjectSet(set) {
|
|
29
31
|
if (!set)
|
|
30
32
|
return undefined;
|
|
@@ -75,6 +77,52 @@ function applyPermissionMode(client, allowed, context = {}) {
|
|
|
75
77
|
}
|
|
76
78
|
return { kind: "deny", mode: "enforce", spanAttributes };
|
|
77
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Single entry point for the pre-tool-use gate. Splits the harness's
|
|
82
|
+
* incoming "tool" into two semantic categories:
|
|
83
|
+
*
|
|
84
|
+
* - **Interactive** — the tool surfaces UI to the user
|
|
85
|
+
* (`AskUserQuestion`, `ExitPlanMode`, `TodoWrite`, plus anything
|
|
86
|
+
* listed in `ORY_INTERACTIVE_TOOLS`). The plugin must not gate these
|
|
87
|
+
* through Ory: the user is the decision-maker, and blocking them in
|
|
88
|
+
* enforce mode (or logging a misleading observe-deny) hides the very
|
|
89
|
+
* prompt the user needs to see. We record one `user.interaction`
|
|
90
|
+
* audit span and return — no permission check, no `tool.invoke`,
|
|
91
|
+
* no `tool.block`.
|
|
92
|
+
*
|
|
93
|
+
* - **Execution** — every other tool. Delegates to
|
|
94
|
+
* {@link checkAndDecide} so observe/enforce/fail-open behavior is
|
|
95
|
+
* identical to the legacy code path.
|
|
96
|
+
*
|
|
97
|
+
* The caller branches on `outcome.kind`. The four execution kinds
|
|
98
|
+
* (`allow`, `deny`, `observe`, `fail_open`) keep their existing
|
|
99
|
+
* semantics; the new `interactive` kind means "do nothing else."
|
|
100
|
+
*/
|
|
101
|
+
async function gateToolCall(client, args) {
|
|
102
|
+
if ((0, tool_catalog_js_1.isInteractiveTool)(args.harness, args.toolName)) {
|
|
103
|
+
client.logger.debug("tool.interactive", {
|
|
104
|
+
harness: args.harness,
|
|
105
|
+
toolName: args.toolName,
|
|
106
|
+
note: "user-interaction primitive — skipping permission check",
|
|
107
|
+
});
|
|
108
|
+
client.tracer.record("user.interaction", "ok", {
|
|
109
|
+
attributes: {
|
|
110
|
+
harness: args.harness,
|
|
111
|
+
toolName: args.toolName,
|
|
112
|
+
kind: "tool",
|
|
113
|
+
...args.spanAttributes,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
kind: "interactive",
|
|
118
|
+
spanAttributes: { interactive: true, toolName: args.toolName },
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return checkAndDecide(client, args.check, {
|
|
122
|
+
spanAttributes: args.spanAttributes,
|
|
123
|
+
modeOverride: args.modeOverride,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
78
126
|
/**
|
|
79
127
|
* Run a permission check and resolve the configured mode against the
|
|
80
128
|
* result. Never throws — fail-open scenarios are surfaced as a typed
|
package/dist/tool-catalog.d.ts
CHANGED
|
@@ -36,3 +36,34 @@ export declare function getToolCatalog(harness: string): readonly string[];
|
|
|
36
36
|
* broad access so the same launcher can run every harness end-to-end.
|
|
37
37
|
*/
|
|
38
38
|
export declare const ALL_TOOLS: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* Per-harness tool names whose semantics are "ask / inform the user"
|
|
41
|
+
* rather than "act on an external system". These reach the pre-tool-use
|
|
42
|
+
* hook the same way real tools do (e.g. Claude Code's `AskUserQuestion`
|
|
43
|
+
* arrives via `PreToolUse`), but they aren't tool *executions* — gating
|
|
44
|
+
* them through Ory would either block the user from being asked or, in
|
|
45
|
+
* observe mode, log a misleading `permission.observe_deny` for an event
|
|
46
|
+
* the user is about to handle directly.
|
|
47
|
+
*
|
|
48
|
+
* Plugins consult this list in their pre-tool gate and short-circuit to
|
|
49
|
+
* a single `user.interaction` audit span when a match is found. See
|
|
50
|
+
* `gateToolCall` in `permissions.ts`.
|
|
51
|
+
*
|
|
52
|
+
* The list is best-effort and additive: operators can extend it at
|
|
53
|
+
* runtime via the `ORY_INTERACTIVE_TOOLS` env var (comma-separated names
|
|
54
|
+
* applied to every harness).
|
|
55
|
+
*/
|
|
56
|
+
export declare const INTERACTIVE_TOOL_CATALOG: Record<string, readonly string[]>;
|
|
57
|
+
/**
|
|
58
|
+
* Names from {@link INTERACTIVE_TOOL_CATALOG} for the given harness,
|
|
59
|
+
* merged with any operator-supplied names from `ORY_INTERACTIVE_TOOLS`.
|
|
60
|
+
* Unknown harnesses still respect the env-var extension.
|
|
61
|
+
*/
|
|
62
|
+
export declare function getInteractiveToolCatalog(harness: string): readonly string[];
|
|
63
|
+
/**
|
|
64
|
+
* Does the named tool, for the given harness, represent a user-facing
|
|
65
|
+
* interaction (ask / inform / confirm) rather than an external-system
|
|
66
|
+
* tool execution? Treats unknown harnesses the same as known ones — the
|
|
67
|
+
* env-var extension still applies.
|
|
68
|
+
*/
|
|
69
|
+
export declare function isInteractiveTool(harness: string, toolName: string): boolean;
|
package/dist/tool-catalog.js
CHANGED
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
* catalog update is safe.
|
|
14
14
|
*/
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = void 0;
|
|
16
|
+
exports.INTERACTIVE_TOOL_CATALOG = exports.ALL_TOOLS = exports.KNOWN_HARNESSES = exports.HARNESS_TOOL_CATALOG = void 0;
|
|
17
17
|
exports.getToolCatalog = getToolCatalog;
|
|
18
|
+
exports.getInteractiveToolCatalog = getInteractiveToolCatalog;
|
|
19
|
+
exports.isInteractiveTool = isInteractiveTool;
|
|
18
20
|
/**
|
|
19
21
|
* Built-in tool names known to ship with each supported harness.
|
|
20
22
|
*/
|
|
@@ -67,3 +69,58 @@ function getToolCatalog(harness) {
|
|
|
67
69
|
* broad access so the same launcher can run every harness end-to-end.
|
|
68
70
|
*/
|
|
69
71
|
exports.ALL_TOOLS = Array.from(new Set(Object.values(exports.HARNESS_TOOL_CATALOG).flat()));
|
|
72
|
+
/**
|
|
73
|
+
* Per-harness tool names whose semantics are "ask / inform the user"
|
|
74
|
+
* rather than "act on an external system". These reach the pre-tool-use
|
|
75
|
+
* hook the same way real tools do (e.g. Claude Code's `AskUserQuestion`
|
|
76
|
+
* arrives via `PreToolUse`), but they aren't tool *executions* — gating
|
|
77
|
+
* them through Ory would either block the user from being asked or, in
|
|
78
|
+
* observe mode, log a misleading `permission.observe_deny` for an event
|
|
79
|
+
* the user is about to handle directly.
|
|
80
|
+
*
|
|
81
|
+
* Plugins consult this list in their pre-tool gate and short-circuit to
|
|
82
|
+
* a single `user.interaction` audit span when a match is found. See
|
|
83
|
+
* `gateToolCall` in `permissions.ts`.
|
|
84
|
+
*
|
|
85
|
+
* The list is best-effort and additive: operators can extend it at
|
|
86
|
+
* runtime via the `ORY_INTERACTIVE_TOOLS` env var (comma-separated names
|
|
87
|
+
* applied to every harness).
|
|
88
|
+
*/
|
|
89
|
+
exports.INTERACTIVE_TOOL_CATALOG = {
|
|
90
|
+
"claude-code": ["AskUserQuestion", "ExitPlanMode", "TodoWrite"],
|
|
91
|
+
codex: [],
|
|
92
|
+
"gemini-cli": [],
|
|
93
|
+
openclaw: [],
|
|
94
|
+
opencode: [],
|
|
95
|
+
};
|
|
96
|
+
function parseEnvInteractiveTools() {
|
|
97
|
+
const raw = process.env.ORY_INTERACTIVE_TOOLS;
|
|
98
|
+
if (!raw)
|
|
99
|
+
return [];
|
|
100
|
+
return raw
|
|
101
|
+
.split(",")
|
|
102
|
+
.map((name) => name.trim())
|
|
103
|
+
.filter((name) => name.length > 0);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Names from {@link INTERACTIVE_TOOL_CATALOG} for the given harness,
|
|
107
|
+
* merged with any operator-supplied names from `ORY_INTERACTIVE_TOOLS`.
|
|
108
|
+
* Unknown harnesses still respect the env-var extension.
|
|
109
|
+
*/
|
|
110
|
+
function getInteractiveToolCatalog(harness) {
|
|
111
|
+
const builtIn = exports.INTERACTIVE_TOOL_CATALOG[harness] ?? [];
|
|
112
|
+
const envExtra = parseEnvInteractiveTools();
|
|
113
|
+
if (envExtra.length === 0)
|
|
114
|
+
return builtIn;
|
|
115
|
+
// Stable order: built-ins first, then env additions, deduplicated.
|
|
116
|
+
return Array.from(new Set([...builtIn, ...envExtra]));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Does the named tool, for the given harness, represent a user-facing
|
|
120
|
+
* interaction (ask / inform / confirm) rather than an external-system
|
|
121
|
+
* tool execution? Treats unknown harnesses the same as known ones — the
|
|
122
|
+
* env-var extension still applies.
|
|
123
|
+
*/
|
|
124
|
+
function isInteractiveTool(harness, toolName) {
|
|
125
|
+
return getInteractiveToolCatalog(harness).includes(toolName);
|
|
126
|
+
}
|
package/dist/tracer.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { EventEmitter } from "node:events";
|
|
20
20
|
import type { SpanExporter } from "./otel/exporter.js";
|
|
21
|
-
export type TraceEvent = "session.start" | "session.end" | "session.verify" | "user.auth" | "user.prompt" | "agent.auth" | "oauth2.introspect" | "oauth2.login" | "oauth2.refresh" | "permission.check" | "permission.batch_check" | "permission.observe_deny" | "tool.invoke" | "tool.complete" | "tool.block" | "tool.fail" | "turn.stop" | "subagent.start" | "subagent.stop" | "notification" | "compaction" | "relationship.create" | "relationship.delete" | "relationship.patch" | "hook.receive" | "hook.passthrough" | "config.resolve";
|
|
21
|
+
export type TraceEvent = "session.start" | "session.end" | "session.verify" | "user.auth" | "user.prompt" | "user.interaction" | "agent.auth" | "oauth2.introspect" | "oauth2.login" | "oauth2.refresh" | "permission.check" | "permission.batch_check" | "permission.observe_deny" | "tool.invoke" | "tool.complete" | "tool.block" | "tool.fail" | "turn.stop" | "subagent.start" | "subagent.stop" | "notification" | "compaction" | "relationship.create" | "relationship.delete" | "relationship.patch" | "hook.receive" | "hook.passthrough" | "config.resolve";
|
|
22
22
|
export type SpanStatus = "ok" | "error" | "denied" | "skipped";
|
|
23
23
|
export interface TraceSpan {
|
|
24
24
|
traceId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/argus",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Ory Argus: the core API for building authentication, authorization, and audit into AI agent harness plugins, extensions, and custom integrations",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://ory.com",
|