@agentrq/codex-gateway 0.1.3 → 0.2.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.
package/README.md CHANGED
@@ -79,6 +79,51 @@ codex-gateway -- codex app-server
79
79
  | Variable | Description | Default |
80
80
  |---|---|---|
81
81
  | `CODEX_MODEL` | Override the model used for all threads/turns | _(codex default)_ |
82
+ | `CODEX_APPROVAL_POLICY` | When Codex asks before acting: `untrusted`, `on-failure`, `on-request`, `never` | `on-request` |
83
+ | `CODEX_SANDBOX` | Sandbox for command execution: `read-only`, `workspace-write`, `danger-full-access` | `read-only` |
84
+
85
+ Unrecognized values fall back to the default and log a warning rather than
86
+ being passed through to Codex.
87
+
88
+ > [!WARNING]
89
+ > `CODEX_APPROVAL_POLICY=never` means Codex never sends an approval request, so
90
+ > tool calls run without reaching agentrq for human review.
91
+ > `CODEX_SANDBOX=danger-full-access` removes the sandbox boundary entirely.
92
+ > The gateway logs a prominent warning when either is set.
93
+
94
+ ### Approvals
95
+
96
+ Codex sends approval requests to the gateway as JSON-RPC requests. The gateway
97
+ forwards each one to agentrq as a `permission_request` and blocks the turn until
98
+ the human answers in the dashboard:
99
+
100
+ | Codex request | Approved | Denied |
101
+ |---|---|---|
102
+ | `item/commandExecution/requestApproval` | `accept` | `decline` |
103
+ | `item/fileChange/requestApproval` | `accept` | `decline` |
104
+ | `item/permissions/requestApproval` | grants the requested profile for the turn | grants nothing |
105
+
106
+ Approval requests identify their subject only by `itemId`, so the gateway
107
+ remembers each item announced via `item/started` and uses it to show the human
108
+ the real command, tool, or file list. Entries are dropped as items complete.
109
+
110
+ agentrq's own MCP tool calls are auto-allowed, matched on the MCP server name
111
+ (`agentrq-<workspaceId>`) rather than on free text, so a shell command that
112
+ merely mentions a workspace id cannot approve itself.
113
+
114
+ ### Elicitations
115
+
116
+ When an MCP server configured in `.codex/config.toml` asks a question
117
+ (`mcpServer/elicitation/request`), the gateway forwards it to agentrq's `elicit`
118
+ tool and returns the human's answer to Codex. Both `form` and `url` modes are
119
+ supported, and the three-action result (`accept` / `decline` / `cancel`) maps
120
+ one-to-one.
121
+
122
+ If the elicitation belongs to a thread with no associated task — it arrived
123
+ outside any task the gateway is running — a task is created and marked ongoing
124
+ so the question still reaches the human. Anything that goes wrong (unsupported
125
+ mode, tool error, unreachable human) resolves to `cancel`, since Codex is
126
+ blocked on a response either way.
82
127
 
83
128
  ### Configuration
84
129
 
@@ -0,0 +1,152 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { resolveThreadPolicy, describeItem, ThreadItemRegistry, DEFAULT_APPROVAL_POLICY, DEFAULT_SANDBOX, } from "../approvals.js";
3
+ describe("resolveThreadPolicy", () => {
4
+ it("should default to the conservative policy/sandbox pair", () => {
5
+ const policy = resolveThreadPolicy({});
6
+ expect(policy.approvalPolicy).toBe(DEFAULT_APPROVAL_POLICY);
7
+ expect(policy.sandbox).toBe(DEFAULT_SANDBOX);
8
+ expect(policy.warnings).toEqual([]);
9
+ });
10
+ it("should honor CODEX_APPROVAL_POLICY", () => {
11
+ const policy = resolveThreadPolicy({ CODEX_APPROVAL_POLICY: "untrusted" });
12
+ expect(policy.approvalPolicy).toBe("untrusted");
13
+ expect(policy.warnings).toEqual([]);
14
+ });
15
+ it("should honor CODEX_SANDBOX", () => {
16
+ const policy = resolveThreadPolicy({ CODEX_SANDBOX: "workspace-write" });
17
+ expect(policy.sandbox).toBe("workspace-write");
18
+ expect(policy.warnings).toEqual([]);
19
+ });
20
+ it("should treat an empty value as unset", () => {
21
+ const policy = resolveThreadPolicy({ CODEX_APPROVAL_POLICY: "", CODEX_SANDBOX: "" });
22
+ expect(policy.approvalPolicy).toBe(DEFAULT_APPROVAL_POLICY);
23
+ expect(policy.sandbox).toBe(DEFAULT_SANDBOX);
24
+ expect(policy.warnings).toEqual([]);
25
+ });
26
+ it("should fall back to the default and warn on an unrecognized policy", () => {
27
+ const policy = resolveThreadPolicy({ CODEX_APPROVAL_POLICY: "yolo" });
28
+ expect(policy.approvalPolicy).toBe(DEFAULT_APPROVAL_POLICY);
29
+ expect(policy.warnings.join(" ")).toContain("CODEX_APPROVAL_POLICY");
30
+ });
31
+ it("should fall back to the default and warn on an unrecognized sandbox", () => {
32
+ const policy = resolveThreadPolicy({ CODEX_SANDBOX: "wide-open" });
33
+ expect(policy.sandbox).toBe(DEFAULT_SANDBOX);
34
+ expect(policy.warnings.join(" ")).toContain("CODEX_SANDBOX");
35
+ });
36
+ it("should warn that a 'never' policy keeps approvals away from the human", () => {
37
+ const policy = resolveThreadPolicy({ CODEX_APPROVAL_POLICY: "never" });
38
+ expect(policy.approvalPolicy).toBe("never");
39
+ expect(policy.warnings.join(" ")).toContain("never request approval");
40
+ });
41
+ it("should warn about a full-access sandbox", () => {
42
+ const policy = resolveThreadPolicy({ CODEX_SANDBOX: "danger-full-access" });
43
+ expect(policy.sandbox).toBe("danger-full-access");
44
+ expect(policy.warnings.join(" ")).toContain("removes the sandbox boundary");
45
+ });
46
+ it("should report both warnings when the pair fully bypasses review", () => {
47
+ const policy = resolveThreadPolicy({
48
+ CODEX_APPROVAL_POLICY: "never",
49
+ CODEX_SANDBOX: "danger-full-access",
50
+ });
51
+ expect(policy.warnings).toHaveLength(2);
52
+ });
53
+ });
54
+ describe("describeItem", () => {
55
+ it("should describe a command execution with its cwd", () => {
56
+ const d = describeItem({
57
+ id: "i1",
58
+ type: "commandExecution",
59
+ command: "npm test",
60
+ cwd: "/repo",
61
+ });
62
+ expect(d).toEqual({
63
+ title: "npm test",
64
+ inputPreview: "npm test\n(cwd: /repo)",
65
+ });
66
+ });
67
+ it("should describe a command execution without a cwd", () => {
68
+ const d = describeItem({ id: "i1", type: "commandExecution", command: "ls" });
69
+ expect(d?.inputPreview).toBe("ls");
70
+ });
71
+ it("should describe an MCP tool call as server.tool and expose the server", () => {
72
+ const d = describeItem({
73
+ id: "i2",
74
+ type: "mcpToolCall",
75
+ server: "agentrq-0cHdAEOUJvN",
76
+ tool: "reply",
77
+ arguments: { chatId: "c1" },
78
+ });
79
+ expect(d?.title).toBe("agentrq-0cHdAEOUJvN.reply");
80
+ expect(d?.server).toBe("agentrq-0cHdAEOUJvN");
81
+ expect(d?.inputPreview).toBe('{"chatId":"c1"}');
82
+ });
83
+ it("should describe a file change by path count and list the paths", () => {
84
+ const d = describeItem({
85
+ id: "i3",
86
+ type: "fileChange",
87
+ changes: [{ path: "/a.ts" }, { path: "/b.ts" }],
88
+ });
89
+ expect(d?.title).toBe("Edit 2 files");
90
+ expect(d?.inputPreview).toBe("/a.ts\n/b.ts");
91
+ });
92
+ it("should singularize a one-file change", () => {
93
+ const d = describeItem({ id: "i4", type: "fileChange", changes: [{ path: "/a.ts" }] });
94
+ expect(d?.title).toBe("Edit 1 file");
95
+ });
96
+ it("should still describe a file change with no readable paths", () => {
97
+ const d = describeItem({ id: "i5", type: "fileChange", changes: [] });
98
+ expect(d?.title).toBe("File change");
99
+ });
100
+ it("should ignore item types that are never approved", () => {
101
+ expect(describeItem({ id: "i6", type: "agentMessage", text: "hi" })).toBeUndefined();
102
+ expect(describeItem({ id: "i7", type: "reasoning" })).toBeUndefined();
103
+ });
104
+ it("should ignore a command execution with no command", () => {
105
+ expect(describeItem({ id: "i8", type: "commandExecution" })).toBeUndefined();
106
+ });
107
+ it("should handle malformed input", () => {
108
+ expect(describeItem(undefined)).toBeUndefined();
109
+ expect(describeItem(null)).toBeUndefined();
110
+ expect(describeItem("nope")).toBeUndefined();
111
+ });
112
+ });
113
+ describe("ThreadItemRegistry", () => {
114
+ it("should record and return a describable item", () => {
115
+ const r = new ThreadItemRegistry();
116
+ r.record({ id: "a", type: "commandExecution", command: "ls" });
117
+ expect(r.get("a")?.title).toBe("ls");
118
+ expect(r.size).toBe(1);
119
+ });
120
+ it("should not record items that cannot be described", () => {
121
+ const r = new ThreadItemRegistry();
122
+ r.record({ id: "a", type: "agentMessage", text: "hi" });
123
+ expect(r.size).toBe(0);
124
+ });
125
+ it("should ignore items without a usable id", () => {
126
+ const r = new ThreadItemRegistry();
127
+ r.record({ type: "commandExecution", command: "ls" });
128
+ r.record({ id: 42, type: "commandExecution", command: "ls" });
129
+ expect(r.size).toBe(0);
130
+ });
131
+ it("should return undefined for an unknown or non-string id", () => {
132
+ const r = new ThreadItemRegistry();
133
+ expect(r.get("missing")).toBeUndefined();
134
+ expect(r.get(undefined)).toBeUndefined();
135
+ expect(r.get(7)).toBeUndefined();
136
+ });
137
+ it("should forget an item so the map stays bounded", () => {
138
+ const r = new ThreadItemRegistry();
139
+ r.record({ id: "a", type: "commandExecution", command: "ls" });
140
+ r.forget("a");
141
+ expect(r.get("a")).toBeUndefined();
142
+ expect(r.size).toBe(0);
143
+ });
144
+ it("should clear every entry", () => {
145
+ const r = new ThreadItemRegistry();
146
+ r.record({ id: "a", type: "commandExecution", command: "ls" });
147
+ r.record({ id: "b", type: "commandExecution", command: "pwd" });
148
+ r.clear();
149
+ expect(r.size).toBe(0);
150
+ });
151
+ });
152
+ //# sourceMappingURL=approvals.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.test.js","sourceRoot":"","sources":["../../src/__tests__/approvals.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { resolveElicitation } from "../elicitation.js";
3
+ function textResult(text, isError = false) {
4
+ return { isError, content: [{ type: "text", text }] };
5
+ }
6
+ describe("resolveElicitation", () => {
7
+ let callTool;
8
+ let deps;
9
+ beforeEach(() => {
10
+ vi.spyOn(console, "error").mockImplementation(() => { });
11
+ callTool = vi.fn();
12
+ deps = { mcpBridge: { callTool }, resolveTaskId: () => "task-1" };
13
+ });
14
+ it("should delegate a form elicitation to the elicit tool with the thread's task", async () => {
15
+ callTool.mockResolvedValue(textResult(JSON.stringify({ action: "accept", content: { name: "mt" } })));
16
+ const result = await resolveElicitation({
17
+ serverName: "some-server",
18
+ threadId: "thr_1",
19
+ mode: "form",
20
+ message: "What is your name?",
21
+ requestedSchema: { type: "object", properties: {} },
22
+ }, deps);
23
+ expect(callTool).toHaveBeenCalledWith("elicit", {
24
+ taskId: "task-1",
25
+ message: "What is your name?",
26
+ mode: "form",
27
+ requestedSchema: { type: "object", properties: {} },
28
+ });
29
+ expect(result).toEqual({ action: "accept", content: { name: "mt" } });
30
+ });
31
+ it("should pass the url through for url-mode elicitations", async () => {
32
+ callTool.mockResolvedValue(textResult(JSON.stringify({ action: "accept" })));
33
+ await resolveElicitation({
34
+ serverName: "s",
35
+ threadId: "thr_1",
36
+ mode: "url",
37
+ message: "Authorize access",
38
+ url: "https://example.test/auth",
39
+ elicitationId: "e1",
40
+ }, deps);
41
+ expect(callTool).toHaveBeenCalledWith("elicit", {
42
+ taskId: "task-1",
43
+ message: "Authorize access",
44
+ mode: "url",
45
+ url: "https://example.test/auth",
46
+ });
47
+ });
48
+ it("should map a declined answer", async () => {
49
+ callTool.mockResolvedValue(textResult(JSON.stringify({ action: "decline" })));
50
+ const r = await resolveElicitation({ threadId: "t", mode: "form", message: "m" }, deps);
51
+ expect(r).toEqual({ action: "decline" });
52
+ });
53
+ it("should map an unrecognized action to cancel", async () => {
54
+ callTool.mockResolvedValue(textResult(JSON.stringify({ action: "whatever" })));
55
+ const r = await resolveElicitation({ threadId: "t", mode: "form", message: "m" }, deps);
56
+ expect(r).toEqual({ action: "cancel" });
57
+ });
58
+ it("should cancel an unsupported mode without calling the tool", async () => {
59
+ const r = await resolveElicitation({ threadId: "t", mode: "telepathy", message: "m" }, deps);
60
+ expect(r).toEqual({ action: "cancel" });
61
+ expect(callTool).not.toHaveBeenCalled();
62
+ });
63
+ it("should cancel when the elicit tool reports an error", async () => {
64
+ callTool.mockResolvedValue(textResult("boom", true));
65
+ const r = await resolveElicitation({ threadId: "t", mode: "form", message: "m" }, deps);
66
+ expect(r).toEqual({ action: "cancel" });
67
+ });
68
+ it("should cancel when the elicit tool returns no content", async () => {
69
+ callTool.mockResolvedValue({ isError: false, content: [] });
70
+ const r = await resolveElicitation({ threadId: "t", mode: "form", message: "m" }, deps);
71
+ expect(r).toEqual({ action: "cancel" });
72
+ });
73
+ it("should cancel when the elicit tool returns unparseable content", async () => {
74
+ callTool.mockResolvedValue(textResult("not json"));
75
+ const r = await resolveElicitation({ threadId: "t", mode: "form", message: "m" }, deps);
76
+ expect(r).toEqual({ action: "cancel" });
77
+ });
78
+ it("should cancel when the tool call throws", async () => {
79
+ callTool.mockRejectedValue(new Error("offline"));
80
+ const r = await resolveElicitation({ threadId: "t", mode: "form", message: "m" }, deps);
81
+ expect(r).toEqual({ action: "cancel" });
82
+ });
83
+ describe("when the thread has no task", () => {
84
+ beforeEach(() => {
85
+ deps.resolveTaskId = () => undefined;
86
+ });
87
+ it("should create an ongoing task and use it", async () => {
88
+ callTool.mockImplementation(async (name) => {
89
+ if (name === "createTask")
90
+ return textResult("Created task id=task-new");
91
+ if (name === "updateTaskStatus")
92
+ return textResult("ok");
93
+ return textResult(JSON.stringify({ action: "accept", content: { a: 1 } }));
94
+ });
95
+ const r = await resolveElicitation({ threadId: "unknown", mode: "form", message: "Pick one", requestedSchema: {} }, deps);
96
+ expect(callTool).toHaveBeenCalledWith("createTask", {
97
+ title: "Pick one",
98
+ body: "Pick one",
99
+ assignee: "human",
100
+ });
101
+ expect(callTool).toHaveBeenCalledWith("updateTaskStatus", {
102
+ taskId: "task-new",
103
+ status: "ongoing",
104
+ });
105
+ expect(callTool).toHaveBeenCalledWith("elicit", expect.objectContaining({ taskId: "task-new" }));
106
+ expect(r).toEqual({ action: "accept", content: { a: 1 } });
107
+ });
108
+ it("should truncate a long message into the task title", async () => {
109
+ const long = "x".repeat(200);
110
+ callTool.mockImplementation(async (name) => {
111
+ if (name === "createTask")
112
+ return textResult("id=t2");
113
+ if (name === "updateTaskStatus")
114
+ return textResult("ok");
115
+ return textResult(JSON.stringify({ action: "decline" }));
116
+ });
117
+ await resolveElicitation({ threadId: "u", mode: "form", message: long }, deps);
118
+ const call = callTool.mock.calls.find((c) => c[0] === "createTask");
119
+ expect(call?.[1].title).toHaveLength(80);
120
+ expect(call?.[1].title.endsWith("...")).toBe(true);
121
+ expect(call?.[1].body).toBe(long);
122
+ });
123
+ it("should cancel when the created task id cannot be parsed", async () => {
124
+ callTool.mockResolvedValue(textResult("something unexpected"));
125
+ const r = await resolveElicitation({ threadId: "u", mode: "form", message: "m" }, deps);
126
+ expect(r).toEqual({ action: "cancel" });
127
+ expect(callTool).not.toHaveBeenCalledWith("elicit", expect.anything());
128
+ });
129
+ it("should cancel when task creation throws", async () => {
130
+ callTool.mockRejectedValue(new Error("nope"));
131
+ const r = await resolveElicitation({ threadId: "u", mode: "form", message: "m" }, deps);
132
+ expect(r).toEqual({ action: "cancel" });
133
+ });
134
+ });
135
+ });
136
+ //# sourceMappingURL=elicitation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elicitation.test.js","sourceRoot":"","sources":["../../src/__tests__/elicitation.test.ts"],"names":[],"mappings":"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"}