agent-office-cli 0.1.2 → 0.1.4
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/package.json +1 -1
- package/src/core/providers/codex.js +34 -9
- package/src/core/providers/codex.test.js +36 -0
- package/src/tunnel.js +38 -1
- package/src/tunnel.test.js +31 -0
package/package.json
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
const { GenericProvider } = require("./generic");
|
|
2
2
|
const { findManagedCodexSessionFile, summarizeCodexSession } = require("./codex-transcript");
|
|
3
3
|
|
|
4
|
+
const APPROVAL_PATTERNS = [
|
|
5
|
+
"approval requested:",
|
|
6
|
+
"approval requested by ",
|
|
7
|
+
"tool call needs your approval",
|
|
8
|
+
"requires approval by policy",
|
|
9
|
+
"requires approval:"
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const IDLE_PATTERNS = [
|
|
13
|
+
"conversation interrupted - tell the model what to do differently",
|
|
14
|
+
"something went wrong? hit `/feedback` to",
|
|
15
|
+
"something went wrong? hit /feedback to"
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const ATTENTION_PATTERNS = [
|
|
19
|
+
"stream disconnected before completion",
|
|
20
|
+
"error sending request for url",
|
|
21
|
+
"network error",
|
|
22
|
+
"connection timeout",
|
|
23
|
+
"timed out",
|
|
24
|
+
"failed to send request",
|
|
25
|
+
"failed to submit",
|
|
26
|
+
"panic"
|
|
27
|
+
];
|
|
28
|
+
|
|
4
29
|
function activeOverlayPatch(session, nextLifecycleState) {
|
|
5
30
|
if (!session || !["approval", "attention"].includes(session.displayState)) {
|
|
6
31
|
return null;
|
|
@@ -48,19 +73,19 @@ class CodexProvider extends GenericProvider {
|
|
|
48
73
|
|
|
49
74
|
classifyOutput(chunk) {
|
|
50
75
|
const text = String(chunk).toLowerCase();
|
|
51
|
-
|
|
76
|
+
|
|
77
|
+
if (IDLE_PATTERNS.some((pattern) => text.includes(pattern))) {
|
|
78
|
+
return "idle";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (APPROVAL_PATTERNS.some((pattern) => text.includes(pattern))) {
|
|
52
82
|
return "approval";
|
|
53
83
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
text.includes("connection timeout") ||
|
|
57
|
-
text.includes("timed out") ||
|
|
58
|
-
text.includes("error") ||
|
|
59
|
-
text.includes("failed") ||
|
|
60
|
-
text.includes("panic")
|
|
61
|
-
) {
|
|
84
|
+
|
|
85
|
+
if (ATTENTION_PATTERNS.some((pattern) => text.includes(pattern))) {
|
|
62
86
|
return "attention";
|
|
63
87
|
}
|
|
88
|
+
|
|
64
89
|
return null;
|
|
65
90
|
}
|
|
66
91
|
|
|
@@ -72,3 +72,39 @@ test("classifyOutput can raise attention for transcript-backed sessions", () =>
|
|
|
72
72
|
|
|
73
73
|
assert.equal(nextState, "attention");
|
|
74
74
|
});
|
|
75
|
+
|
|
76
|
+
test("classifyOutput treats user interrupted Codex screens as idle", () => {
|
|
77
|
+
const provider = new CodexProvider();
|
|
78
|
+
const nextState = provider.classifyOutput(
|
|
79
|
+
"Conversation interrupted - tell the model what to do differently. Something went wrong? Hit `/feedback` to report the issue."
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
assert.equal(nextState, "idle");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("classifyOutput treats stream disconnects as attention", () => {
|
|
86
|
+
const provider = new CodexProvider();
|
|
87
|
+
const nextState = provider.classifyOutput(
|
|
88
|
+
"stream disconnected before completion: error sending request for url (http://54.255.64.152:3000/openai/responses)"
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
assert.equal(nextState, "attention");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("classifyOutput does not treat plain explanatory approval text as a real approval prompt", () => {
|
|
95
|
+
const provider = new CodexProvider();
|
|
96
|
+
const nextState = provider.classifyOutput(
|
|
97
|
+
"只有真实审批提示才归到 approval"
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
assert.equal(nextState, null);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("classifyOutput recognizes real Codex approval prompts", () => {
|
|
104
|
+
const provider = new CodexProvider();
|
|
105
|
+
const nextState = provider.classifyOutput(
|
|
106
|
+
"Approval requested: Codex wants to edit files"
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
assert.equal(nextState, "approval");
|
|
110
|
+
});
|
package/src/tunnel.js
CHANGED
|
@@ -3,6 +3,42 @@ const { toSessionSummary } = require("./core");
|
|
|
3
3
|
|
|
4
4
|
const RECONNECT_BASE_MS = 1000;
|
|
5
5
|
const RECONNECT_MAX_MS = 30000;
|
|
6
|
+
const LOCAL_PROXY_STRIP_HEADERS = new Set([
|
|
7
|
+
"accept-encoding",
|
|
8
|
+
"connection",
|
|
9
|
+
"content-length",
|
|
10
|
+
"cookie",
|
|
11
|
+
"origin",
|
|
12
|
+
"referer",
|
|
13
|
+
"te",
|
|
14
|
+
"trailer",
|
|
15
|
+
"transfer-encoding",
|
|
16
|
+
"upgrade"
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
function shouldStripLocalProxyHeader(name) {
|
|
20
|
+
return (
|
|
21
|
+
LOCAL_PROXY_STRIP_HEADERS.has(name) ||
|
|
22
|
+
name.startsWith("proxy-") ||
|
|
23
|
+
name.startsWith("sec-") ||
|
|
24
|
+
name.startsWith("x-forwarded-")
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildLocalRequestHeaders(headers, localServerUrl) {
|
|
29
|
+
const nextHeaders = {};
|
|
30
|
+
|
|
31
|
+
for (const [name, rawValue] of Object.entries(headers || {})) {
|
|
32
|
+
const key = String(name).toLowerCase();
|
|
33
|
+
if (shouldStripLocalProxyHeader(key) || rawValue == null) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
nextHeaders[key] = Array.isArray(rawValue) ? rawValue.join(", ") : String(rawValue);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
nextHeaders.host = new URL(localServerUrl).host;
|
|
40
|
+
return nextHeaders;
|
|
41
|
+
}
|
|
6
42
|
|
|
7
43
|
function createTunnelClient({ key, relayUrl, localServerUrl }) {
|
|
8
44
|
let ws = null;
|
|
@@ -116,7 +152,7 @@ function createTunnelClient({ key, relayUrl, localServerUrl }) {
|
|
|
116
152
|
const fetchUrl = `${localServerUrl}${msg.path}`;
|
|
117
153
|
const fetchOptions = {
|
|
118
154
|
method: msg.method || "GET",
|
|
119
|
-
headers:
|
|
155
|
+
headers: buildLocalRequestHeaders(msg.headers, localServerUrl)
|
|
120
156
|
};
|
|
121
157
|
if (msg.body && msg.method !== "GET" && msg.method !== "HEAD") {
|
|
122
158
|
fetchOptions.body = msg.body;
|
|
@@ -220,5 +256,6 @@ function createTunnelClient({ key, relayUrl, localServerUrl }) {
|
|
|
220
256
|
}
|
|
221
257
|
|
|
222
258
|
module.exports = {
|
|
259
|
+
buildLocalRequestHeaders,
|
|
223
260
|
createTunnelClient
|
|
224
261
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const test = require("node:test");
|
|
2
|
+
const assert = require("node:assert/strict");
|
|
3
|
+
|
|
4
|
+
const { buildLocalRequestHeaders } = require("./tunnel");
|
|
5
|
+
|
|
6
|
+
test("buildLocalRequestHeaders strips browser-only proxy headers and rewrites host", () => {
|
|
7
|
+
const next = buildLocalRequestHeaders(
|
|
8
|
+
{
|
|
9
|
+
authorization: "Bearer token",
|
|
10
|
+
accept: "*/*",
|
|
11
|
+
"content-type": "application/json",
|
|
12
|
+
host: "agentoffice.top",
|
|
13
|
+
connection: "keep-alive",
|
|
14
|
+
"accept-encoding": "gzip, deflate, br, zstd",
|
|
15
|
+
"content-length": "123",
|
|
16
|
+
origin: "https://agentoffice.top",
|
|
17
|
+
referer: "https://agentoffice.top/office",
|
|
18
|
+
"sec-ch-ua": "\"Chromium\";v=\"146\"",
|
|
19
|
+
"sec-fetch-mode": "cors",
|
|
20
|
+
"x-forwarded-for": "203.0.113.10"
|
|
21
|
+
},
|
|
22
|
+
"http://127.0.0.1:8765"
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
assert.deepEqual(next, {
|
|
26
|
+
authorization: "Bearer token",
|
|
27
|
+
accept: "*/*",
|
|
28
|
+
"content-type": "application/json",
|
|
29
|
+
host: "127.0.0.1:8765"
|
|
30
|
+
});
|
|
31
|
+
});
|