@optimuslabs/runtime 0.3.4 → 0.3.12
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.js +36 -70
- package/dist/index.js.map +1 -1
- package/dist/lib/client-event.d.ts.map +1 -1
- package/dist/lib/client-event.js +35 -25
- package/dist/lib/client-event.js.map +1 -1
- package/dist/lib/login.d.ts +15 -0
- package/dist/lib/login.d.ts.map +1 -0
- package/dist/lib/login.js +109 -0
- package/dist/lib/login.js.map +1 -0
- package/dist/lib/pii-scrub.d.ts +9 -0
- package/dist/lib/pii-scrub.d.ts.map +1 -0
- package/dist/lib/pii-scrub.js +17 -0
- package/dist/lib/pii-scrub.js.map +1 -0
- package/dist/lib/prompt-collection-policy.d.ts +10 -0
- package/dist/lib/prompt-collection-policy.d.ts.map +1 -1
- package/dist/lib/prompt-collection-policy.js +42 -6
- package/dist/lib/prompt-collection-policy.js.map +1 -1
- package/dist/lib/startup-sync.d.ts.map +1 -1
- package/dist/lib/startup-sync.js +78 -56
- package/dist/lib/startup-sync.js.map +1 -1
- package/dist/lib/telemetry/build-event.d.ts.map +1 -1
- package/dist/lib/telemetry/build-event.js +73 -6
- package/dist/lib/telemetry/build-event.js.map +1 -1
- package/dist/lib/telemetry/canonical.d.ts +1 -1
- package/dist/lib/telemetry/canonical.d.ts.map +1 -1
- package/dist/lib/telemetry/canonical.js +23 -14
- package/dist/lib/telemetry/canonical.js.map +1 -1
- package/dist/lib/telemetry/index.d.ts +1 -1
- package/dist/lib/telemetry/index.d.ts.map +1 -1
- package/dist/lib/telemetry/index.js +1 -1
- package/dist/lib/telemetry/index.js.map +1 -1
- package/dist/lib/telemetry/prompt-privacy.d.ts +7 -1
- package/dist/lib/telemetry/prompt-privacy.d.ts.map +1 -1
- package/dist/lib/telemetry/prompt-privacy.js +13 -3
- package/dist/lib/telemetry/prompt-privacy.js.map +1 -1
- package/dist/lib/telemetry/types.d.ts +18 -0
- package/dist/lib/telemetry/types.d.ts.map +1 -1
- package/dist/lib/telemetry/wire-metadata.d.ts +21 -6
- package/dist/lib/telemetry/wire-metadata.d.ts.map +1 -1
- package/dist/lib/telemetry/wire-metadata.js +51 -19
- package/dist/lib/telemetry/wire-metadata.js.map +1 -1
- package/dist/lib/tofu.d.ts +1 -0
- package/dist/lib/tofu.d.ts.map +1 -1
- package/dist/lib/tofu.js +1 -1
- package/dist/lib/tofu.js.map +1 -1
- package/dist/lib/transcript.d.ts +33 -0
- package/dist/lib/transcript.d.ts.map +1 -0
- package/dist/lib/transcript.js +217 -0
- package/dist/lib/transcript.js.map +1 -0
- package/dist/test/canonical.test.js +40 -0
- package/dist/test/canonical.test.js.map +1 -1
- package/dist/test/client-event-scrub.test.d.ts +2 -0
- package/dist/test/client-event-scrub.test.d.ts.map +1 -0
- package/dist/test/client-event-scrub.test.js +36 -0
- package/dist/test/client-event-scrub.test.js.map +1 -0
- package/dist/test/login.test.d.ts +2 -0
- package/dist/test/login.test.d.ts.map +1 -0
- package/dist/test/login.test.js +60 -0
- package/dist/test/login.test.js.map +1 -0
- package/dist/test/org-fingerprint-key.test.d.ts +2 -0
- package/dist/test/org-fingerprint-key.test.d.ts.map +1 -0
- package/dist/test/org-fingerprint-key.test.js +39 -0
- package/dist/test/org-fingerprint-key.test.js.map +1 -0
- package/dist/test/org-scrub-setting.test.d.ts +2 -0
- package/dist/test/org-scrub-setting.test.d.ts.map +1 -0
- package/dist/test/org-scrub-setting.test.js +42 -0
- package/dist/test/org-scrub-setting.test.js.map +1 -0
- package/dist/test/prompt-privacy.test.js +19 -2
- package/dist/test/prompt-privacy.test.js.map +1 -1
- package/dist/test/startup-sync-scrub.test.d.ts +2 -0
- package/dist/test/startup-sync-scrub.test.d.ts.map +1 -0
- package/dist/test/startup-sync-scrub.test.js +36 -0
- package/dist/test/startup-sync-scrub.test.js.map +1 -0
- package/dist/test/telemetry.test.js +62 -0
- package/dist/test/telemetry.test.js.map +1 -1
- package/dist/test/transcript.test.d.ts +2 -0
- package/dist/test/transcript.test.d.ts.map +1 -0
- package/dist/test/transcript.test.js +162 -0
- package/dist/test/transcript.test.js.map +1 -0
- package/dist/test/wire-metadata.test.d.ts +2 -0
- package/dist/test/wire-metadata.test.d.ts.map +1 -0
- package/dist/test/wire-metadata.test.js +110 -0
- package/dist/test/wire-metadata.test.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { orgConfigPath, persistCollectPromptsFromResponse, readCollectPrompts, readFingerprintKey, } from "../lib/prompt-collection-policy.js";
|
|
7
|
+
const ORG_KEY = "cd".repeat(32);
|
|
8
|
+
function withHome(run) {
|
|
9
|
+
const saved = process.env.HOME;
|
|
10
|
+
process.env.HOME = fs.mkdtempSync(path.join(os.tmpdir(), "org-key-"));
|
|
11
|
+
try {
|
|
12
|
+
run();
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
process.env.HOME = saved;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
test("no org key until a response has carried one", () => {
|
|
19
|
+
withHome(() => assert.strictEqual(readFingerprintKey(), undefined));
|
|
20
|
+
});
|
|
21
|
+
test("the org key a response carries is stored beside the prompt setting, owner-only", () => {
|
|
22
|
+
withHome(() => {
|
|
23
|
+
persistCollectPromptsFromResponse({ collect_prompts: false, fingerprint_key: ORG_KEY });
|
|
24
|
+
assert.strictEqual(readFingerprintKey(), ORG_KEY);
|
|
25
|
+
assert.strictEqual(readCollectPrompts(), false);
|
|
26
|
+
assert.strictEqual(fs.statSync(orgConfigPath()).mode & 0o777, 0o600);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
test("a response without the key keeps the stored one, and a malformed key is ignored", () => {
|
|
30
|
+
withHome(() => {
|
|
31
|
+
persistCollectPromptsFromResponse({ fingerprint_key: ORG_KEY });
|
|
32
|
+
persistCollectPromptsFromResponse({ collect_prompts: true });
|
|
33
|
+
persistCollectPromptsFromResponse({ fingerprint_key: "not-hex" });
|
|
34
|
+
persistCollectPromptsFromResponse({ fingerprint_key: "ab" });
|
|
35
|
+
assert.strictEqual(readFingerprintKey(), ORG_KEY);
|
|
36
|
+
assert.strictEqual(readCollectPrompts(), true);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=org-fingerprint-key.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"org-fingerprint-key.test.js","sourceRoot":"","sources":["../../src/test/org-fingerprint-key.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,aAAa,EACb,iCAAiC,EACjC,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,oCAAoC,CAAC;AAE5C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAEhC,SAAS,QAAQ,CAAC,GAAe;IAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;IACtE,IAAI,CAAC;QACH,GAAG,EAAE,CAAC;IACR,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;IACvD,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,kBAAkB,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gFAAgF,EAAE,GAAG,EAAE;IAC1F,QAAQ,CAAC,GAAG,EAAE;QACZ,iCAAiC,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC;QACxF,MAAM,CAAC,WAAW,CAAC,kBAAkB,EAAE,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,CAAC,WAAW,CAAC,kBAAkB,EAAE,EAAE,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC,IAAI,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iFAAiF,EAAE,GAAG,EAAE;IAC3F,QAAQ,CAAC,GAAG,EAAE;QACZ,iCAAiC,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC;QAChE,iCAAiC,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,iCAAiC,CAAC,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC,CAAC;QAClE,iCAAiC,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,MAAM,CAAC,WAAW,CAAC,kBAAkB,EAAE,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,CAAC,WAAW,CAAC,kBAAkB,EAAE,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"org-scrub-setting.test.d.ts","sourceRoot":"","sources":["../../src/test/org-scrub-setting.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { orgConfigPath, persistCollectPromptsFromResponse } from "../lib/prompt-collection-policy.js";
|
|
7
|
+
import { scrubText } from "../lib/pii-scrub.js";
|
|
8
|
+
// OPT-942: the organization's scrubbing setting arrives on the startup and inventory responses
|
|
9
|
+
// and reaches the scrubber through the org config file. HOME is a temporary directory.
|
|
10
|
+
const SECRET = `ghp_${"C".repeat(36)}`;
|
|
11
|
+
function withHome(run) {
|
|
12
|
+
const saved = process.env.HOME;
|
|
13
|
+
const home = fs.mkdtempSync(path.join(os.tmpdir(), "org-scrub-"));
|
|
14
|
+
process.env.HOME = home;
|
|
15
|
+
try {
|
|
16
|
+
run();
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
process.env.HOME = saved;
|
|
20
|
+
fs.rmSync(home, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
test("before any response the runtime scrubs", () => {
|
|
24
|
+
withHome(() => assert.strictEqual(scrubText(SECRET), "ghp_[OL_REDACTED:GITHUB_TOKEN]"));
|
|
25
|
+
});
|
|
26
|
+
test("a response switching scrubbing off stops it, and one switching it on restarts it", () => {
|
|
27
|
+
withHome(() => {
|
|
28
|
+
persistCollectPromptsFromResponse({ collect_prompts: false, scrub_pii: false });
|
|
29
|
+
assert.strictEqual(JSON.parse(fs.readFileSync(orgConfigPath(), "utf8")).scrub_pii, false);
|
|
30
|
+
assert.strictEqual(scrubText(SECRET), SECRET);
|
|
31
|
+
persistCollectPromptsFromResponse({ collect_prompts: false, scrub_pii: true });
|
|
32
|
+
assert.strictEqual(scrubText(SECRET), "ghp_[OL_REDACTED:GITHUB_TOKEN]");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
test("a response without the setting keeps the stored one", () => {
|
|
36
|
+
withHome(() => {
|
|
37
|
+
persistCollectPromptsFromResponse({ scrub_pii: false });
|
|
38
|
+
persistCollectPromptsFromResponse({ collect_prompts: true });
|
|
39
|
+
assert.strictEqual(scrubText(SECRET), SECRET);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=org-scrub-setting.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"org-scrub-setting.test.js","sourceRoot":"","sources":["../../src/test/org-scrub-setting.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,iCAAiC,EAAE,MAAM,oCAAoC,CAAC;AACtG,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,+FAA+F;AAC/F,uFAAuF;AACvF,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AAEvC,SAAS,QAAQ,CAAC,GAAe;IAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IAC/B,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC;QACH,GAAG,EAAE,CAAC;IACR,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC;QACzB,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;IAClD,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,gCAAgC,CAAC,CAAC,CAAC;AAC1F,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kFAAkF,EAAE,GAAG,EAAE;IAC5F,QAAQ,CAAC,GAAG,EAAE;QACZ,iCAAiC,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QAChF,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC1F,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QAE9C,iCAAiC,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/E,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,gCAAgC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,QAAQ,CAAC,GAAG,EAAE;QACZ,iCAAiC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACxD,iCAAiC,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { test } from "node:test";
|
|
2
2
|
import assert from "node:assert";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
|
-
import { applyPromptCollectionToMetadata, stripRawEventPromptFields } from "../lib/telemetry/prompt-privacy.js";
|
|
3
|
+
import { createHash, createHmac } from "node:crypto";
|
|
4
|
+
import { applyPromptCollectionToMetadata, hashUserPrompt, stripRawEventPromptFields } from "../lib/telemetry/prompt-privacy.js";
|
|
5
5
|
test("stripRawEventPromptFields removes top-level prompt keys only", () => {
|
|
6
6
|
const stripped = stripRawEventPromptFields({
|
|
7
7
|
user_prompt: "secret",
|
|
@@ -30,4 +30,21 @@ test("applyPromptCollectionToMetadata keeps plaintext when hashing is off", () =
|
|
|
30
30
|
assert.strictEqual(out.user_prompt_hash, undefined);
|
|
31
31
|
assert.strictEqual(out.raw_event.user_prompt, undefined);
|
|
32
32
|
});
|
|
33
|
+
const ORG_KEY = "ab".repeat(32);
|
|
34
|
+
test("applyPromptCollectionToMetadata keys the prompt hash under the org key once it has arrived", () => {
|
|
35
|
+
const keyed = createHmac("sha256", Buffer.from(ORG_KEY, "hex")).update("do a thing", "utf8").digest("hex");
|
|
36
|
+
const out = applyPromptCollectionToMetadata({ user_prompt: " do a thing " }, true, ORG_KEY);
|
|
37
|
+
assert.strictEqual(out.user_prompt_hash, keyed);
|
|
38
|
+
assert.notStrictEqual(out.user_prompt_hash, createHash("sha256").update("do a thing", "utf8").digest("hex"));
|
|
39
|
+
assert.match(String(out.user_prompt_hash), /^[0-9a-f]{64}$/);
|
|
40
|
+
assert.strictEqual(out.user_prompt, undefined);
|
|
41
|
+
});
|
|
42
|
+
test("hashUserPrompt stays plain SHA-256 until the org key has arrived", () => {
|
|
43
|
+
assert.strictEqual(hashUserPrompt("yes"), createHash("sha256").update("yes", "utf8").digest("hex"));
|
|
44
|
+
assert.strictEqual(hashUserPrompt("yes", undefined), hashUserPrompt("yes"));
|
|
45
|
+
});
|
|
46
|
+
test("a short prompt's keyed hash cannot be matched by hashing guesses without the key", () => {
|
|
47
|
+
const guesses = ["yes", "no", "ok", "continue"].map((g) => createHash("sha256").update(g, "utf8").digest("hex"));
|
|
48
|
+
assert.ok(!guesses.includes(hashUserPrompt("yes", ORG_KEY)));
|
|
49
|
+
});
|
|
33
50
|
//# sourceMappingURL=prompt-privacy.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-privacy.test.js","sourceRoot":"","sources":["../../src/test/prompt-privacy.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"prompt-privacy.test.js","sourceRoot":"","sources":["../../src/test/prompt-privacy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-sync-scrub.test.d.ts","sourceRoot":"","sources":["../../src/test/startup-sync-scrub.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { ensureStartupMetadataSynced } from "../lib/startup-sync.js";
|
|
7
|
+
test("ensureStartupMetadataSynced - a token in the git remote is scrubbed, and the scrubber version is sent", async () => {
|
|
8
|
+
const token = `ghp_${"C".repeat(36)}`;
|
|
9
|
+
const saved = {
|
|
10
|
+
home: process.env.HOME,
|
|
11
|
+
repo: process.env.GITHUB_REPOSITORY,
|
|
12
|
+
interval: process.env.OPTIMUS_STARTUP_SYNC_INTERVAL_MS,
|
|
13
|
+
fetch: globalThis.fetch,
|
|
14
|
+
};
|
|
15
|
+
process.env.HOME = fs.mkdtempSync(path.join(os.tmpdir(), "startup-sync-"));
|
|
16
|
+
process.env.GITHUB_REPOSITORY = `origin\thttps://x-access-token:${token}@github.com/acme/app.git (fetch)`;
|
|
17
|
+
process.env.OPTIMUS_STARTUP_SYNC_INTERVAL_MS = "0";
|
|
18
|
+
let sent;
|
|
19
|
+
globalThis.fetch = (async (_url, init) => {
|
|
20
|
+
sent = JSON.parse(String(init?.body));
|
|
21
|
+
return new Response(JSON.stringify({ status: "ok" }));
|
|
22
|
+
});
|
|
23
|
+
try {
|
|
24
|
+
await ensureStartupMetadataSynced("hw-startup-scrub-test");
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
process.env.HOME = saved.home;
|
|
28
|
+
process.env.GITHUB_REPOSITORY = saved.repo;
|
|
29
|
+
process.env.OPTIMUS_STARTUP_SYNC_INTERVAL_MS = saved.interval;
|
|
30
|
+
globalThis.fetch = saved.fetch;
|
|
31
|
+
}
|
|
32
|
+
assert.strictEqual(sent?.metadata.repo_identifier, "origin\thttps://x-access-token:[OL_REDACTED:URL_CREDENTIALS]@github.com/acme/app.git (fetch)");
|
|
33
|
+
assert.match(String(sent?.metadata.scrubber_version), /^\d+\.\d+\.\d+$/);
|
|
34
|
+
assert.ok(!JSON.stringify(sent).includes(token));
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=startup-sync-scrub.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"startup-sync-scrub.test.js","sourceRoot":"","sources":["../../src/test/startup-sync-scrub.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAErE,IAAI,CAAC,uGAAuG,EAAE,KAAK,IAAI,EAAE;IACvH,MAAM,KAAK,GAAG,OAAO,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG;QACZ,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI;QACtB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;QACnC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,gCAAgC;QACtD,KAAK,EAAE,UAAU,CAAC,KAAK;KACxB,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IAC3E,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,kCAAkC,KAAK,kCAAkC,CAAC;IAC1G,OAAO,CAAC,GAAG,CAAC,gCAAgC,GAAG,GAAG,CAAC;IACnD,IAAI,IAAuD,CAAC;IAC5D,UAAU,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,IAAa,EAAE,IAAyB,EAAE,EAAE;QACrE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACtC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC,CAAiB,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,2BAA2B,CAAC,uBAAuB,CAAC,CAAC;IAC7D,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,KAAK,CAAC,IAAI,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,gCAAgC,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC9D,UAAU,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACjC,CAAC;IACD,MAAM,CAAC,WAAW,CAChB,IAAI,EAAE,QAAQ,CAAC,eAAe,EAC9B,8FAA8F,CAC/F,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,iBAAiB,CAAC,CAAC;IACzE,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { test } from "node:test";
|
|
2
2
|
import assert from "node:assert";
|
|
3
|
+
import os from "node:os";
|
|
3
4
|
import { buildTelemetryEvent, eventToDict } from "../lib/telemetry.js";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
4
7
|
test("buildTelemetryEvent - null input returns null", () => {
|
|
5
8
|
assert.strictEqual(buildTelemetryEvent(null), null);
|
|
6
9
|
assert.strictEqual(buildTelemetryEvent(undefined), null);
|
|
@@ -198,4 +201,63 @@ test("buildTelemetryEvent - glob normal path raw", () => {
|
|
|
198
201
|
const event = buildTelemetryEvent(inv);
|
|
199
202
|
assert(event);
|
|
200
203
|
});
|
|
204
|
+
test("buildTelemetryEvent - carries every hook's hash from OPTIMUS_HOOK_MANIFEST", () => {
|
|
205
|
+
const saved = process.env.OPTIMUS_HOOK_MANIFEST;
|
|
206
|
+
try {
|
|
207
|
+
process.env.OPTIMUS_HOOK_MANIFEST = JSON.stringify({
|
|
208
|
+
"claude/optimus-tool-call": "a".repeat(64),
|
|
209
|
+
"claude/optimus-hook-common": "b".repeat(64),
|
|
210
|
+
"claude/empty": "",
|
|
211
|
+
});
|
|
212
|
+
const event = buildTelemetryEvent({ tool_name: "Read", tool_input: {} }, "claude");
|
|
213
|
+
assert.deepStrictEqual(event?.tool_call_metadata.hook_manifest, {
|
|
214
|
+
"claude/optimus-tool-call": "a".repeat(64),
|
|
215
|
+
"claude/optimus-hook-common": "b".repeat(64),
|
|
216
|
+
});
|
|
217
|
+
process.env.OPTIMUS_HOOK_MANIFEST = "not json";
|
|
218
|
+
const malformed = buildTelemetryEvent({ tool_name: "Read", tool_input: {} }, "claude");
|
|
219
|
+
assert.strictEqual(malformed?.tool_call_metadata.hook_manifest, undefined);
|
|
220
|
+
delete process.env.OPTIMUS_HOOK_MANIFEST;
|
|
221
|
+
const absent = buildTelemetryEvent({ tool_name: "Read", tool_input: {} }, "claude");
|
|
222
|
+
assert.strictEqual(absent?.tool_call_metadata.hook_manifest, undefined);
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
if (saved !== undefined)
|
|
226
|
+
process.env.OPTIMUS_HOOK_MANIFEST = saved;
|
|
227
|
+
else
|
|
228
|
+
delete process.env.OPTIMUS_HOOK_MANIFEST;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
test("buildTelemetryEvent - every event carries the macOS account running it", () => {
|
|
232
|
+
const event = buildTelemetryEvent({ tool_name: "Read", tool_input: { file_path: "/tmp/x" } });
|
|
233
|
+
const { uid, username } = os.userInfo();
|
|
234
|
+
assert.deepStrictEqual(event?.tool_call_metadata.os_user, { uid, username });
|
|
235
|
+
});
|
|
236
|
+
function codexSession(originator) {
|
|
237
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "codex-session-"));
|
|
238
|
+
const file = path.join(dir, "rollout.jsonl");
|
|
239
|
+
const meta = { type: "session_meta", payload: { originator, cli_version: "0.166.0" } };
|
|
240
|
+
fs.writeFileSync(file, JSON.stringify(meta) + "\n");
|
|
241
|
+
return file;
|
|
242
|
+
}
|
|
243
|
+
test("buildTelemetryEvent - a Codex event says which product ran the tool", () => {
|
|
244
|
+
const inv = {
|
|
245
|
+
tool_name: "Bash",
|
|
246
|
+
tool_input: {},
|
|
247
|
+
raw_event: { transcript_path: codexSession("codex_work_desktop") },
|
|
248
|
+
};
|
|
249
|
+
const event = buildTelemetryEvent(inv, "codex");
|
|
250
|
+
assert.deepStrictEqual(event?.tool_call_metadata.agent_product, {
|
|
251
|
+
originator: "codex_work_desktop",
|
|
252
|
+
version: "0.166.0",
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
test("buildTelemetryEvent - other agents send no product", () => {
|
|
256
|
+
const inv = {
|
|
257
|
+
tool_name: "Bash",
|
|
258
|
+
tool_input: {},
|
|
259
|
+
raw_event: { transcript_path: codexSession("codex-tui") },
|
|
260
|
+
};
|
|
261
|
+
assert.strictEqual(buildTelemetryEvent(inv, "claude")?.tool_call_metadata.agent_product, undefined);
|
|
262
|
+
});
|
|
201
263
|
//# sourceMappingURL=telemetry.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.test.js","sourceRoot":"","sources":["../../src/test/telemetry.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"telemetry.test.js","sourceRoot":"","sources":["../../src/test/telemetry.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.test.d.ts","sourceRoot":"","sources":["../../src/test/transcript.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { copilotSessionEventsPath, readAgentProduct, readCopilotProduct, readLastUserPrompt, } from "../lib/transcript.js";
|
|
7
|
+
function transcript(lines) {
|
|
8
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "transcript-"));
|
|
9
|
+
const file = path.join(dir, "t.jsonl");
|
|
10
|
+
fs.writeFileSync(file, lines.map((l) => JSON.stringify(l)).join("\n") + "\n");
|
|
11
|
+
return file;
|
|
12
|
+
}
|
|
13
|
+
const codexUser = (text) => ({
|
|
14
|
+
type: "response_item",
|
|
15
|
+
payload: { type: "message", role: "user", content: [{ type: "input_text", text }] },
|
|
16
|
+
});
|
|
17
|
+
test("readLastUserPrompt - Codex: the last prompt the person typed", () => {
|
|
18
|
+
const file = transcript([
|
|
19
|
+
codexUser("# AGENTS.md instructions for /repo"),
|
|
20
|
+
codexUser("<environment_context>\n <cwd>/repo</cwd>"),
|
|
21
|
+
codexUser("first question"),
|
|
22
|
+
{ type: "response_item", payload: { type: "function_call", name: "shell" } },
|
|
23
|
+
codexUser("Run ls -la /tmp"),
|
|
24
|
+
]);
|
|
25
|
+
assert.strictEqual(readLastUserPrompt(file), "Run ls -la /tmp");
|
|
26
|
+
});
|
|
27
|
+
test("readLastUserPrompt - Codex: injected context is never taken for the prompt", () => {
|
|
28
|
+
const file = transcript([codexUser("real prompt"), codexUser("<environment_context>x")]);
|
|
29
|
+
assert.strictEqual(readLastUserPrompt(file), "real prompt");
|
|
30
|
+
});
|
|
31
|
+
test("readLastUserPrompt - Claude transcripts still read as before", () => {
|
|
32
|
+
const file = transcript([{ type: "user", message: { role: "user", content: "claude prompt" } }]);
|
|
33
|
+
assert.strictEqual(readLastUserPrompt(file), "claude prompt");
|
|
34
|
+
});
|
|
35
|
+
test("readLastUserPrompt - missing file is null", () => {
|
|
36
|
+
assert.strictEqual(readLastUserPrompt("/nonexistent/t.jsonl"), null);
|
|
37
|
+
});
|
|
38
|
+
const claudeUser = (text) => ({ type: "user", message: { role: "user", content: text } });
|
|
39
|
+
const toolResult = (output) => ({
|
|
40
|
+
type: "user",
|
|
41
|
+
message: { role: "user", content: [{ type: "tool_result", content: output }] },
|
|
42
|
+
});
|
|
43
|
+
test("readLastUserPrompt - the prompt is found behind more than one read of later tool output", () => {
|
|
44
|
+
const output = "x".repeat(64 * 1024);
|
|
45
|
+
const file = transcript([
|
|
46
|
+
claudeUser("older prompt"),
|
|
47
|
+
claudeUser("latest prompt"),
|
|
48
|
+
...Array.from({ length: 80 }, () => toolResult(output)), // about 5 MB
|
|
49
|
+
]);
|
|
50
|
+
assert.strictEqual(readLastUserPrompt(file), "latest prompt");
|
|
51
|
+
});
|
|
52
|
+
test("readLastUserPrompt - a prompt across a read boundary keeps its multi-byte characters", () => {
|
|
53
|
+
const text = "é🙂".repeat(200 * 1024); // about 1.2 MB of multi-byte characters
|
|
54
|
+
const file = transcript([claudeUser("earlier"), claudeUser(text), toolResult("y".repeat(1536 * 1024))]);
|
|
55
|
+
assert.strictEqual(readLastUserPrompt(file), text);
|
|
56
|
+
});
|
|
57
|
+
test("readLastUserPrompt - the last line needs no trailing newline", () => {
|
|
58
|
+
const file = transcript([]);
|
|
59
|
+
fs.writeFileSync(file, JSON.stringify(claudeUser("no newline after me")));
|
|
60
|
+
assert.strictEqual(readLastUserPrompt(file), "no newline after me");
|
|
61
|
+
});
|
|
62
|
+
test("readLastUserPrompt - an empty transcript has no prompt", () => {
|
|
63
|
+
assert.strictEqual(readLastUserPrompt(transcript([])), null);
|
|
64
|
+
});
|
|
65
|
+
test("readLastUserPrompt - a transcript past Node's string limit still gives its prompt", () => {
|
|
66
|
+
// Days in one session grew a transcript past 1 GB; reading it whole threw, and every tool call
|
|
67
|
+
// went out without its prompt. A sparse 600 MB of old history stands in for it.
|
|
68
|
+
const file = transcript([]);
|
|
69
|
+
fs.truncateSync(file, 600 * 1024 * 1024);
|
|
70
|
+
const recent = [claudeUser("still found"), toolResult("ls output")];
|
|
71
|
+
fs.appendFileSync(file, "\n" + recent.map((l) => JSON.stringify(l)).join("\n") + "\n");
|
|
72
|
+
try {
|
|
73
|
+
assert.strictEqual(readLastUserPrompt(file), "still found");
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
fs.rmSync(path.dirname(file), { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
const sessionMeta = (originator, extra = {}) => ({
|
|
80
|
+
timestamp: "2026-11-03T11:51:30.567Z",
|
|
81
|
+
type: "session_meta",
|
|
82
|
+
payload: {
|
|
83
|
+
id: "01a1a13f",
|
|
84
|
+
cwd: "/Users/alan/Documents/Codex/2026-11-03/work",
|
|
85
|
+
originator,
|
|
86
|
+
cli_version: "0.166.0-alpha.5.1",
|
|
87
|
+
// Codex puts its whole instruction set on this line, so it is long.
|
|
88
|
+
base_instructions: { text: "x".repeat(20000) },
|
|
89
|
+
...extra,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
test("readAgentProduct - the product and engine version that wrote a Codex session", () => {
|
|
93
|
+
const file = transcript([sessionMeta("codex_work_desktop"), codexUser("hi")]);
|
|
94
|
+
assert.deepStrictEqual(readAgentProduct(file), {
|
|
95
|
+
originator: "codex_work_desktop",
|
|
96
|
+
version: "0.166.0-alpha.5.1",
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
test("readAgentProduct - a product name with a space, as the Codex app writes it", () => {
|
|
100
|
+
const file = transcript([sessionMeta("Codex Desktop")]);
|
|
101
|
+
assert.strictEqual(readAgentProduct(file)?.originator, "Codex Desktop");
|
|
102
|
+
});
|
|
103
|
+
test("readAgentProduct - null for a transcript that is not a Codex session", () => {
|
|
104
|
+
const file = transcript([{ type: "user", message: { role: "user", content: "originator" } }]);
|
|
105
|
+
assert.strictEqual(readAgentProduct(file), null);
|
|
106
|
+
});
|
|
107
|
+
test("readAgentProduct - null for a missing file", () => {
|
|
108
|
+
assert.strictEqual(readAgentProduct("/nonexistent/session.jsonl"), null);
|
|
109
|
+
});
|
|
110
|
+
function copilotHome(sessionId, clientName, copilotVersion) {
|
|
111
|
+
const home = fs.mkdtempSync(path.join(os.tmpdir(), "copilot-home-"));
|
|
112
|
+
const dir = path.join(home, ".copilot", "session-state", sessionId);
|
|
113
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
114
|
+
fs.writeFileSync(path.join(dir, "workspace.yaml"), `id: ${sessionId}\nclient_name: ${clientName}\nname: x\n`);
|
|
115
|
+
const start = { type: "session.start", data: { sessionId, producer: "copilot-agent", copilotVersion } };
|
|
116
|
+
fs.writeFileSync(path.join(dir, "events.jsonl"), JSON.stringify(start) + "\n");
|
|
117
|
+
return home;
|
|
118
|
+
}
|
|
119
|
+
test("readCopilotProduct - the CLI and the Copilot app, from the session's workspace.yaml", () => {
|
|
120
|
+
const cli = copilotHome("a4825a9b-e723-47a9", "github/cli", "1.0.105");
|
|
121
|
+
assert.deepStrictEqual(readCopilotProduct("a4825a9b-e723-47a9", null, cli), {
|
|
122
|
+
originator: "github/cli",
|
|
123
|
+
version: "1.0.105",
|
|
124
|
+
});
|
|
125
|
+
const app = copilotHome("4cb827b2-e1e8-4a09", "github/autopilot", "1.0.94-3");
|
|
126
|
+
assert.strictEqual(readCopilotProduct("4cb827b2-e1e8-4a09", null, app)?.originator, "github/autopilot");
|
|
127
|
+
});
|
|
128
|
+
test("readCopilotProduct - VS Code's Copilot Chat, from its transcript path", () => {
|
|
129
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "vscode-"));
|
|
130
|
+
const transcripts = path.join(dir, "workspaceStorage", "ws", "GitHub.copilot-chat", "transcripts");
|
|
131
|
+
fs.mkdirSync(transcripts, { recursive: true });
|
|
132
|
+
const file = path.join(transcripts, "7716c62e.jsonl");
|
|
133
|
+
fs.writeFileSync(file, JSON.stringify({ type: "session.start", data: { copilotVersion: "0.64.0" } }) + "\n");
|
|
134
|
+
assert.deepStrictEqual(readCopilotProduct("7716c62e", file), { originator: "vscode", version: "0.64.0" });
|
|
135
|
+
});
|
|
136
|
+
test("readCopilotProduct - null for an unknown session or an id that is not one", () => {
|
|
137
|
+
const home = copilotHome("known-session-1", "github/cli", "1.0.105");
|
|
138
|
+
assert.strictEqual(readCopilotProduct("unknown-session-1", null, home), null);
|
|
139
|
+
assert.strictEqual(readCopilotProduct("../../etc/passwd", null, home), null);
|
|
140
|
+
});
|
|
141
|
+
const copilotUser = (content) => ({ type: "user.message", data: { content, transformedContent: `<x>${content}</x>` } });
|
|
142
|
+
test("readLastUserPrompt - Copilot CLI: the last prompt the person typed, from the session's events", () => {
|
|
143
|
+
const file = transcript([
|
|
144
|
+
{ type: "session.start", data: { copilotVersion: "1.0.105" } },
|
|
145
|
+
copilotUser("first question"),
|
|
146
|
+
{ type: "assistant.message", data: { content: "answer" } },
|
|
147
|
+
copilotUser('can you echo "copilot probe"'),
|
|
148
|
+
{ type: "tool.execution_start", data: { toolName: "bash" } },
|
|
149
|
+
]);
|
|
150
|
+
assert.strictEqual(readLastUserPrompt(file), 'can you echo "copilot probe"');
|
|
151
|
+
});
|
|
152
|
+
test("readLastUserPrompt - Copilot CLI: an empty user message is not taken for the prompt", () => {
|
|
153
|
+
const file = transcript([copilotUser("real prompt"), copilotUser(" ")]);
|
|
154
|
+
assert.strictEqual(readLastUserPrompt(file), "real prompt");
|
|
155
|
+
});
|
|
156
|
+
test("copilotSessionEventsPath - the session's events file, or null for an id that is not one", () => {
|
|
157
|
+
assert.strictEqual(copilotSessionEventsPath("869f6bad-3d83-436f", "/Users/x"), "/Users/x/.copilot/session-state/869f6bad-3d83-436f/events.jsonl");
|
|
158
|
+
assert.strictEqual(copilotSessionEventsPath("../../etc/passwd", "/Users/x"), null);
|
|
159
|
+
assert.strictEqual(copilotSessionEventsPath(null, "/Users/x"), null);
|
|
160
|
+
assert.strictEqual(copilotSessionEventsPath("869f6bad-3d83-436f", ""), null);
|
|
161
|
+
});
|
|
162
|
+
//# sourceMappingURL=transcript.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.test.js","sourceRoot":"","sources":["../../src/test/transcript.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire-metadata.test.d.ts","sourceRoot":"","sources":["../../src/test/wire-metadata.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { persistCollectPromptsFromResponse } from "../lib/prompt-collection-policy.js";
|
|
7
|
+
import { cloneToolCallMetadataForWire } from "../lib/telemetry/wire-metadata.js";
|
|
8
|
+
// Assembled at runtime so no real-looking key sits in the source.
|
|
9
|
+
const ANTHROPIC = `sk-ant-api03-${"A".repeat(40)}`;
|
|
10
|
+
test("cloneToolCallMetadataForWire - secrets in the content never leave the machine; emails and paths go as they are", () => {
|
|
11
|
+
const command = `curl -H "x-api-key: ${ANTHROPIC}" https://api.anthropic.com && mail ada@gmail.com`;
|
|
12
|
+
const wire = cloneToolCallMetadataForWire({
|
|
13
|
+
tool_name: "Bash",
|
|
14
|
+
tool_input: { command },
|
|
15
|
+
raw_event: { hook_event_name: "PreToolUse", command, cwd: "/Users/ada/src" },
|
|
16
|
+
user_prompt: `use OPENAI_API_KEY=sk-${"b".repeat(30)} please`,
|
|
17
|
+
});
|
|
18
|
+
const expected = 'curl -H "x-api-key: sk-ant-api03-[OL_REDACTED:ANTHROPIC_KEY]" https://api.anthropic.com && mail ada@gmail.com';
|
|
19
|
+
assert.deepStrictEqual(wire?.tool_input, { command: expected });
|
|
20
|
+
assert.deepStrictEqual(wire?.raw_event, { hook_event_name: "PreToolUse", command: expected });
|
|
21
|
+
assert.strictEqual(wire?.user_prompt, "use OPENAI_API_KEY=sk-[OL_REDACTED:OPENAI_KEY] please");
|
|
22
|
+
assert.ok(!JSON.stringify(wire).includes(ANTHROPIC));
|
|
23
|
+
assert.match(String(wire?.scrubber_version), /^\d+\.\d+\.\d+$/);
|
|
24
|
+
});
|
|
25
|
+
test("cloneToolCallMetadataForWire - server-owned keys are still dropped", () => {
|
|
26
|
+
const wire = cloneToolCallMetadataForWire({ tool_name: "Read", user_profile: { email: "x@y.z" }, action_type: "read" });
|
|
27
|
+
assert.deepStrictEqual(Object.keys(wire ?? {}).sort(), ["scrubber_version", "tool_name"]);
|
|
28
|
+
});
|
|
29
|
+
test("cloneToolCallMetadataForWire - vendor secrets get the org's fingerprint once its key is stored", () => {
|
|
30
|
+
const saved = process.env.HOME;
|
|
31
|
+
process.env.HOME = fs.mkdtempSync(path.join(os.tmpdir(), "wire-key-"));
|
|
32
|
+
try {
|
|
33
|
+
const command = `export ANTHROPIC_API_KEY=${ANTHROPIC}`;
|
|
34
|
+
assert.deepStrictEqual(cloneToolCallMetadataForWire({ tool_input: { command } })?.tool_input, {
|
|
35
|
+
command: "export ANTHROPIC_API_KEY=sk-ant-api03-[OL_REDACTED:ANTHROPIC_KEY]",
|
|
36
|
+
});
|
|
37
|
+
persistCollectPromptsFromResponse({ fingerprint_key: "ef".repeat(32) });
|
|
38
|
+
const keyed = cloneToolCallMetadataForWire({ tool_input: { command } })?.tool_input;
|
|
39
|
+
assert.match(keyed.command, /^export ANTHROPIC_API_KEY=sk-ant-api03-\[OL_REDACTED:ANTHROPIC_KEY:[0-9a-f]{24}\]$/);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
process.env.HOME = saved;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
test("cloneToolCallMetadataForWire - the running identity is sent as it is, never scrubbed", () => {
|
|
46
|
+
const identity = {
|
|
47
|
+
login: { tenant_id: "org-uuid", email: "ada@acme.io" },
|
|
48
|
+
os_user: { uid: 501, username: "ada.lovelace" },
|
|
49
|
+
};
|
|
50
|
+
const wire = cloneToolCallMetadataForWire({ tool_name: "Read", ...identity });
|
|
51
|
+
assert.deepStrictEqual(wire?.login, identity.login);
|
|
52
|
+
assert.deepStrictEqual(wire?.os_user, identity.os_user);
|
|
53
|
+
});
|
|
54
|
+
test("cloneToolCallMetadataForWire - only allowlisted fields are sent; a field added later stays on the Mac", () => {
|
|
55
|
+
const wire = cloneToolCallMetadataForWire({
|
|
56
|
+
tool_name: "Write",
|
|
57
|
+
tool_input: { file_path: "/tmp/a.txt", content: "hello" },
|
|
58
|
+
hook_hash: "a".repeat(64),
|
|
59
|
+
some_future_field: { anything: "x" },
|
|
60
|
+
user_profile: { email: "spoof@x.io" },
|
|
61
|
+
action_type: "spoofed",
|
|
62
|
+
});
|
|
63
|
+
assert.deepStrictEqual(Object.keys(wire ?? {}).sort(), ["hook_hash", "scrubber_version", "tool_input", "tool_name"]);
|
|
64
|
+
});
|
|
65
|
+
test("cloneToolCallMetadataForWire - raw_event keeps only what the backend reads", () => {
|
|
66
|
+
const wire = cloneToolCallMetadataForWire({
|
|
67
|
+
tool_name: "Shell",
|
|
68
|
+
raw_event: {
|
|
69
|
+
hookEventName: "beforeShellExecution",
|
|
70
|
+
tool_name: "Shell",
|
|
71
|
+
command: `curl -H "Authorization: Bearer ${ANTHROPIC}" https://x.io`,
|
|
72
|
+
tool_response: { stdout: "file contents" },
|
|
73
|
+
transcript_path: "/Users/ada/.cursor/transcript.jsonl",
|
|
74
|
+
tool_input: { content: "the agent's own copy" },
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
assert.deepStrictEqual(wire?.raw_event, {
|
|
78
|
+
hookEventName: "beforeShellExecution",
|
|
79
|
+
tool_name: "Shell",
|
|
80
|
+
command: 'curl -H "Authorization: Bearer sk-ant-api03-[OL_REDACTED:ANTHROPIC_KEY]" https://x.io',
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
test("cloneToolCallMetadataForWire - a Cursor write of a file with a key in it is sent scrubbed", () => {
|
|
84
|
+
const wire = cloneToolCallMetadataForWire({
|
|
85
|
+
tool_name: "write",
|
|
86
|
+
tool_input: { file_path: ".env", content: `OPENAI_API_KEY=sk-proj-${"c".repeat(40)}\nDEBUG=1\n` },
|
|
87
|
+
});
|
|
88
|
+
assert.deepStrictEqual(wire?.tool_input, {
|
|
89
|
+
file_path: ".env",
|
|
90
|
+
content: "OPENAI_API_KEY=sk-proj-[OL_REDACTED:OPENAI_KEY]\nDEBUG=1\n",
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
test("cloneToolCallMetadataForWire - home directories, the running account's or another's, are not redacted", () => {
|
|
94
|
+
const wire = cloneToolCallMetadataForWire({
|
|
95
|
+
tool_name: "Read",
|
|
96
|
+
tool_input: { file_path: "/Users/ada/.claude/plugins/cache/tools/deploy/1.0.0/skills/deploy/SKILL.md" },
|
|
97
|
+
user_prompt: "Base directory for this skill: /Users/ada/.claude/skills/deploy and see /Users/bob/notes",
|
|
98
|
+
os_user: { uid: 501, username: "ada" },
|
|
99
|
+
});
|
|
100
|
+
assert.deepStrictEqual(wire?.tool_input, {
|
|
101
|
+
file_path: "/Users/ada/.claude/plugins/cache/tools/deploy/1.0.0/skills/deploy/SKILL.md",
|
|
102
|
+
});
|
|
103
|
+
assert.strictEqual(wire?.user_prompt, "Base directory for this skill: /Users/ada/.claude/skills/deploy and see /Users/bob/notes");
|
|
104
|
+
});
|
|
105
|
+
test("cloneToolCallMetadataForWire - the agent product (originator) is sent as it is", () => {
|
|
106
|
+
const agent_product = { originator: "codex_work", version: "1.2.3" };
|
|
107
|
+
const wire = cloneToolCallMetadataForWire({ tool_name: "Bash", agent_product });
|
|
108
|
+
assert.deepStrictEqual(wire?.agent_product, agent_product);
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=wire-metadata.test.js.map
|