@ory/amp 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 +2 -2
- package/dist/permission.d.ts +7 -4
- package/dist/permission.js +56 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ npx -y -p @ory/amp ory-amp uninstall # remove the delegate, plugin, and skills
|
|
|
49
49
|
|
|
50
50
|
From any project where you'd like Ory authentication, inside Amp:
|
|
51
51
|
|
|
52
|
-
1. **Start a local Ory instance.** Ask Amp *"start the local Ory stack"* or
|
|
52
|
+
1. **Start a local Ory instance.** Ask Amp *"start the local Ory stack"* or invoke the `ory-local-up` skill. A banner prints the seeded test user's email and password — note them.
|
|
53
53
|
2. **Scaffold Ory into your project.** Ask Amp *"add Ory auth to this app"* (the `ory-auth-setup` skill). It installs Ory Elements, wires the SDK, and generates the login / registration / recovery / settings pages, all targeting the local stack.
|
|
54
54
|
3. **Sign in.** Start your app, visit the login page, and sign in with the seeded credentials. You now have a real Ory session backed by a real Ory stack — locally, offline, zero configuration.
|
|
55
55
|
|
|
@@ -59,7 +59,7 @@ Continue to [Agent security](#agent-security) when you're ready to enforce.
|
|
|
59
59
|
|
|
60
60
|
Amp's Ory integration is a **hybrid** of two mechanisms:
|
|
61
61
|
|
|
62
|
-
- **Blocking gate — a permission delegate helper (`ory-amp-permission`).** Registered in `amp.permissions`, Amp invokes it per tool call with the tool params on stdin and decides by **exit code**: `0` = allow, `1` = ask, `≥2` = reject (stderr is surfaced to the model). This is the only part that can block. Amp enforces a 10-second delegate timeout, so the Ory check fails open (allows)
|
|
62
|
+
- **Blocking gate — a permission delegate helper (`ory-amp-permission`).** Registered in `amp.permissions`, Amp invokes it per tool call with the tool params on stdin and decides by **exit code**: `0` = allow, `1` = ask, `≥2` = reject (stderr is surfaced to the model). This is the only part that can block. Amp enforces a 10-second delegate timeout and treats a delegate that outlives it as a reject, so the delegate applies its own deadline to the Ory permission check (default 5 seconds, configurable via `ORY_AMP_CHECK_TIMEOUT_MS`) and fails open (allows) when the check is slow, hanging, or unreachable.
|
|
63
63
|
- **Advisory in-process plugin (`.amp/plugins/ory.ts`).** Discovered by Amp's Bun runtime, it handles session-start auth (advisory — it cannot hard-block at session start) and post-tool audit tracing.
|
|
64
64
|
|
|
65
65
|
The phases map as:
|
package/dist/permission.d.ts
CHANGED
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
* here a denial is exit 2 *plus a stderr message*, and stdout is unused.
|
|
17
17
|
*
|
|
18
18
|
* TIMEOUT: Amp enforces a 10-second delegate timeout. A delegate that does
|
|
19
|
-
* not exit within 10s is treated as a reject, so
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* not exit within 10s is treated as a reject, so a hanging Ory backend
|
|
20
|
+
* would otherwise surface as a block. To keep infra failures fail-open,
|
|
21
|
+
* the permission check below is raced against a local deadline (default
|
|
22
|
+
* 5000ms, override via ORY_AMP_CHECK_TIMEOUT_MS); on timeout the delegate
|
|
23
|
+
* logs a warning to stderr and allows (exit 0), well inside Amp's window.
|
|
23
24
|
*
|
|
24
25
|
* Fail-open: any error — parse failure, network error, rate limit, crash —
|
|
25
26
|
* resolves to exit 0 (allow). The delegate must never wedge the agent.
|
|
@@ -35,6 +36,8 @@ export interface DelegateDecision {
|
|
|
35
36
|
/** Reason to write to stderr (only set when rejecting). */
|
|
36
37
|
reason?: string;
|
|
37
38
|
}
|
|
39
|
+
/** Resolve the check deadline, overridable via ORY_AMP_CHECK_TIMEOUT_MS. */
|
|
40
|
+
export declare function resolveCheckTimeoutMs(): number;
|
|
38
41
|
/** Pull the tool name out of the (undocumented) delegate payload. */
|
|
39
42
|
export declare function extractToolName(input: AmpDelegateInput): string;
|
|
40
43
|
/**
|
package/dist/permission.js
CHANGED
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
* here a denial is exit 2 *plus a stderr message*, and stdout is unused.
|
|
18
18
|
*
|
|
19
19
|
* TIMEOUT: Amp enforces a 10-second delegate timeout. A delegate that does
|
|
20
|
-
* not exit within 10s is treated as a reject, so
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* not exit within 10s is treated as a reject, so a hanging Ory backend
|
|
21
|
+
* would otherwise surface as a block. To keep infra failures fail-open,
|
|
22
|
+
* the permission check below is raced against a local deadline (default
|
|
23
|
+
* 5000ms, override via ORY_AMP_CHECK_TIMEOUT_MS); on timeout the delegate
|
|
24
|
+
* logs a warning to stderr and allows (exit 0), well inside Amp's window.
|
|
24
25
|
*
|
|
25
26
|
* Fail-open: any error — parse failure, network error, rate limit, crash —
|
|
26
27
|
* resolves to exit 0 (allow). The delegate must never wedge the agent.
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
* and 10s timeout).
|
|
30
31
|
*/
|
|
31
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.resolveCheckTimeoutMs = resolveCheckTimeoutMs;
|
|
32
34
|
exports.extractToolName = extractToolName;
|
|
33
35
|
exports.decideToolPermission = decideToolPermission;
|
|
34
36
|
const argus_1 = require("@ory/argus");
|
|
@@ -36,6 +38,44 @@ const types_js_1 = require("./types.js");
|
|
|
36
38
|
function resolveNamespace() {
|
|
37
39
|
return process.env.ORY_PERMISSION_NAMESPACE ?? "AgentTools";
|
|
38
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Local deadline for the Ory permission check. Amp treats a delegate that
|
|
43
|
+
* does not exit within 10s as a REJECT, so a hanging backend must be cut
|
|
44
|
+
* off locally and resolved as allow (fail-open for infra failures). The
|
|
45
|
+
* default leaves ample headroom inside Amp's 10s window.
|
|
46
|
+
*/
|
|
47
|
+
const DEFAULT_CHECK_TIMEOUT_MS = 5000;
|
|
48
|
+
/** Resolve the check deadline, overridable via ORY_AMP_CHECK_TIMEOUT_MS. */
|
|
49
|
+
function resolveCheckTimeoutMs() {
|
|
50
|
+
const raw = process.env.ORY_AMP_CHECK_TIMEOUT_MS;
|
|
51
|
+
if (raw) {
|
|
52
|
+
const parsed = Number(raw);
|
|
53
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
return DEFAULT_CHECK_TIMEOUT_MS;
|
|
57
|
+
}
|
|
58
|
+
/** Sentinel resolved by the deadline arm of the race below. */
|
|
59
|
+
const CHECK_TIMED_OUT = Symbol("ory-amp-check-timeout");
|
|
60
|
+
/**
|
|
61
|
+
* Race a promise against the local check deadline. The timer is unref'd
|
|
62
|
+
* (and cleared once the race settles) so it never keeps the process alive.
|
|
63
|
+
*/
|
|
64
|
+
async function raceCheckDeadline(work, timeoutMs) {
|
|
65
|
+
let timer;
|
|
66
|
+
try {
|
|
67
|
+
return await Promise.race([
|
|
68
|
+
work,
|
|
69
|
+
new Promise((resolve) => {
|
|
70
|
+
timer = setTimeout(() => resolve(CHECK_TIMED_OUT), timeoutMs);
|
|
71
|
+
timer.unref?.();
|
|
72
|
+
}),
|
|
73
|
+
]);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
39
79
|
/** Pull the tool name out of the (undocumented) delegate payload. */
|
|
40
80
|
function extractToolName(input) {
|
|
41
81
|
return input.tool ?? input.toolName ?? input.name ?? "unknown";
|
|
@@ -68,7 +108,11 @@ async function decideToolPermission(input, client) {
|
|
|
68
108
|
const subject = (0, argus_1.resolveUserSubject)(client, sessionId ? `session:${sessionId}` : undefined);
|
|
69
109
|
const subjectId = (0, argus_1.subjectLabel)(subject);
|
|
70
110
|
try {
|
|
71
|
-
|
|
111
|
+
// Race against a local deadline: core's checkPermission has no HTTP
|
|
112
|
+
// timeout, and Amp treats a delegate that outlives its 10s window as
|
|
113
|
+
// a reject — a hanging backend must fail OPEN, not closed.
|
|
114
|
+
const timeoutMs = resolveCheckTimeoutMs();
|
|
115
|
+
const raced = await raceCheckDeadline((0, argus_1.gateToolCall)(client, {
|
|
72
116
|
harness: "amp",
|
|
73
117
|
toolName,
|
|
74
118
|
check: {
|
|
@@ -78,7 +122,13 @@ async function decideToolPermission(input, client) {
|
|
|
78
122
|
...subject,
|
|
79
123
|
},
|
|
80
124
|
spanAttributes: { toolName },
|
|
81
|
-
});
|
|
125
|
+
}), timeoutMs);
|
|
126
|
+
if (raced === CHECK_TIMED_OUT) {
|
|
127
|
+
process.stderr.write(`[ory-agent] permission check for "${toolName}" timed out after ${timeoutMs}ms; allowing (fail-open)\n`);
|
|
128
|
+
client.logger.warn("delegate.check_timeout", { toolName, timeoutMs });
|
|
129
|
+
return { exitCode: types_js_1.AMP_DELEGATE_EXIT.ALLOW };
|
|
130
|
+
}
|
|
131
|
+
const outcome = raced;
|
|
82
132
|
// Interactive tools (operator-extensible via ORY_INTERACTIVE_TOOLS):
|
|
83
133
|
// the user.interaction span is already recorded; allow so Amp can
|
|
84
134
|
// surface the prompt to the user.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ory/amp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Ory plugin for Amp (Sourcegraph's coding agent): a permission delegate that authorizes every tool call plus an in-process plugin for session auth and audit tracing",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://ory.com",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"!dist/**/*.tsbuildinfo"
|
|
68
68
|
],
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@ory/argus": "0.
|
|
70
|
+
"@ory/argus": "0.11.1"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"typescript": "^6.0.2",
|