@sema-agent/core 1.446.0 → 1.448.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/dist/bin/sema-tb.js +3 -3
- package/dist/bin/tb-env.d.ts +1 -0
- package/dist/bin/tb-env.js +3 -0
- package/dist/core/arg-summary.js +14 -10
- package/dist/core/secret-env.js +3 -2
- package/dist/core/untrusted-egress.js +53 -12
- package/package.json +4 -1
package/dist/bin/sema-tb.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readFileSync, realpathSync, createWriteStream } from "node:fs";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { Runner, NodeExecutionEnv, createOpenAIBrain, createAnthropicBrain, CODE_SYSTEM_PROMPT, assembleCodeTools, makeWebFetchSummarizer, } from "../index.js";
|
|
5
|
-
import { positiveIntFromEnv } from "./tb-env.js";
|
|
5
|
+
import { positiveIntFromEnv, tbModelInput } from "./tb-env.js";
|
|
6
6
|
import { inferMaxTokensField } from "../brain/openai.js";
|
|
7
7
|
function usage(code = 2) {
|
|
8
8
|
const out = code === 0 ? process.stdout : process.stderr;
|
|
@@ -12,7 +12,7 @@ function usage(code = 2) {
|
|
|
12
12
|
" WARNING: argv is visible to every process in the sandbox (ps / pgrep -f / /proc/*/cmdline) — long\n" +
|
|
13
13
|
" or sensitive instructions should ride --objective-file or stdin, never --message.\n" +
|
|
14
14
|
"Env: <PROVIDER>_API_KEY / <PROVIDER>_BASE_URL, TB_MAX_TURNS, TB_TIMEOUT_SEC, TB_FULLBODY, TB_TODOREMIND, TB_BGTASKS, TB_SUMMARIZE,\n" +
|
|
15
|
-
" TB_FINALVERIFY, TB_CALLCAP, TB_FINALIZE, TB_REASONING, TB_JSONL, TB_CONTEXT_WINDOW, TB_MAX_OUTPUT_TOKENS, TB_TASKLIST\n" +
|
|
15
|
+
" TB_FINALVERIFY, TB_CALLCAP, TB_FINALIZE, TB_REASONING, TB_JSONL, TB_CONTEXT_WINDOW, TB_MAX_OUTPUT_TOKENS, TB_TASKLIST, TB_VISION\n" +
|
|
16
16
|
' TB_COST="input,output[,cacheRead[,cacheWrite]]" — USD per MTok; explicit pricing override (omitted\n' +
|
|
17
17
|
" trailing segments default to 0). Without it, pricing resolves by MODEL FAMILY (deepseek/claude/gpt-o*)\n" +
|
|
18
18
|
" first, then the wire-prefix table; unknown model on an overridden BASE_URL ⇒ cost accounting disabled.\n");
|
|
@@ -185,7 +185,7 @@ function buildModelAndBrain(providerModel) {
|
|
|
185
185
|
provider,
|
|
186
186
|
baseUrl: baseUrlOverride || d.baseUrl,
|
|
187
187
|
reasoning: process.env.TB_REASONING === "0" ? false : process.env.TB_REASONING === "1" ? true : (d?.reasoning ?? false),
|
|
188
|
-
input:
|
|
188
|
+
input: tbModelInput(),
|
|
189
189
|
cost: costRes.cost,
|
|
190
190
|
contextWindow: contextWindowOverride ?? d?.contextWindow ?? 200_000,
|
|
191
191
|
maxTokens: maxOutputTokensOverride ?? d?.maxTokens ?? 32_000,
|
package/dist/bin/tb-env.d.ts
CHANGED
package/dist/bin/tb-env.js
CHANGED
package/dist/core/arg-summary.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
const ACTIVITY_ARG_MAX = 80;
|
|
2
2
|
const SENSITIVE_KEY = /token|secret|key|password|passwd|credential|auth/i;
|
|
3
3
|
const SECRET_PATTERNS = [
|
|
4
|
-
/(?<![A-Za-z0-9])(?:sk|pk|rk|gh[opsur])[-_][A-Za-z0-9_-]{8,}/g,
|
|
5
|
-
/(?<![A-Za-z0-9])AIza[A-Za-z0-9_-]{20,}/g,
|
|
6
|
-
/(?<![A-Za-z0-9])xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
7
|
-
/(?<![A-Za-z0-9])AKIA[0-9A-Z]{12,}/g,
|
|
8
|
-
/(?<![A-Za-z0-9])
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/(?<![A-Za-z0-9])
|
|
4
|
+
{ re: /(?<![A-Za-z0-9])(?:sk|pk|rk|gh[opsur])[-_][A-Za-z0-9_-]{8,}/g, replace: "[redacted]" },
|
|
5
|
+
{ re: /(?<![A-Za-z0-9])AIza[A-Za-z0-9_-]{20,}/g, replace: "[redacted]" },
|
|
6
|
+
{ re: /(?<![A-Za-z0-9])xox[baprs]-[A-Za-z0-9-]{10,}/g, replace: "[redacted]" },
|
|
7
|
+
{ re: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{12,}/g, replace: "[redacted]" },
|
|
8
|
+
{ re: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{20,}/g, replace: "[redacted]" },
|
|
9
|
+
{ re: /(?<![A-Za-z0-9])eyJ[A-Za-z0-9._-]{20,}/g, replace: "[redacted]" },
|
|
10
|
+
{ re: /-----BEGIN[ A-Z]*PRIVATE KEY-----[\s\S]*?(?:-----END[ A-Z]*PRIVATE KEY-----|$)/g, replace: "[redacted]" },
|
|
11
|
+
{ re: /(?<![A-Za-z0-9])Bearer\s+[A-Za-z0-9._~+/-]{8,}=*/gi, replace: "[redacted]" },
|
|
12
|
+
{
|
|
13
|
+
re: /(?<![A-Za-z0-9])((?:token|secret|key|password|passwd|credential|authorization)["']?\s*[=:]\s*["']?)[^\s"';|&]{4,}/gi,
|
|
14
|
+
replace: "$1[redacted]",
|
|
15
|
+
},
|
|
12
16
|
];
|
|
13
17
|
export function scrubSecrets(s) {
|
|
14
18
|
let out = s;
|
|
15
|
-
for (const
|
|
16
|
-
out = out.replace(
|
|
19
|
+
for (const { re, replace } of SECRET_PATTERNS)
|
|
20
|
+
out = out.replace(re, replace);
|
|
17
21
|
return out;
|
|
18
22
|
}
|
|
19
23
|
function truncCodePoints(s) {
|
package/dist/core/secret-env.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
const SECRET_ENV_RE = /(^|_)(KEY|TOKEN|SECRET|SECRETS|PASSWORD|PASSWD|CREDENTIAL|CREDENTIALS|PRIVATEKEY|APIKEY|ACCESS_KEY|SESSION_TOKEN)
|
|
1
|
+
const SECRET_ENV_RE = /(^|_)(KEY|TOKEN|SECRET|SECRETS|PASSWORD|PASSWD|CREDENTIAL|CREDENTIALS|PRIVATEKEY|APIKEY|ACCESS_KEY|SESSION_TOKEN)(?:_(?:\d+|ID))?$/i;
|
|
2
|
+
const SECRET_ENV_EXACT_NAMES = new Set(["DATABASE_URL", "CONNECTION_STRING", "DSN", "NPM_AUTH"]);
|
|
2
3
|
export function isSecretEnvKey(key) {
|
|
3
|
-
return SECRET_ENV_RE.test(key);
|
|
4
|
+
return SECRET_ENV_RE.test(key) || SECRET_ENV_EXACT_NAMES.has(key.toUpperCase());
|
|
4
5
|
}
|
|
5
6
|
export function scrubSecretEnv(env) {
|
|
6
7
|
const out = {};
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import { delimitUntrusted } from "./untrusted-text.js";
|
|
2
2
|
import { scrubSecrets } from "./arg-summary.js";
|
|
3
3
|
const URI_SCHEME = String.raw `[A-Za-z][A-Za-z0-9+.\-_]{0,63}`;
|
|
4
|
-
const CREDENTIALS_IN_AUTHORITY = new RegExp(String.raw `(${URI_SCHEME}:\/\/)(?:[^/\s"'@]*@)
|
|
4
|
+
const CREDENTIALS_IN_AUTHORITY = new RegExp(String.raw `(${URI_SCHEME}:\/\/)((?:[^/\s"'@]*@)+)`, "gi");
|
|
5
5
|
const URL_OF_ANY_PROTOCOL = new RegExp(String.raw `${URI_SCHEME}:\/\/[^\s"'\`]+`, "gi");
|
|
6
|
+
const BARE_USERNAME_MAX_LEN = 20;
|
|
7
|
+
function redactUserinfo(scheme, userinfoWithAt) {
|
|
8
|
+
const userinfo = userinfoWithAt.slice(0, -1);
|
|
9
|
+
if (userinfo.includes("@"))
|
|
10
|
+
return `${scheme}[redacted-credentials]@`;
|
|
11
|
+
const colonIdx = userinfo.indexOf(":");
|
|
12
|
+
if (colonIdx === -1) {
|
|
13
|
+
return userinfo.length >= BARE_USERNAME_MAX_LEN ? `${scheme}[redacted-credentials]@` : `${scheme}${userinfo}@`;
|
|
14
|
+
}
|
|
15
|
+
return `${scheme}${userinfo.slice(0, colonIdx)}:[redacted-credentials]@`;
|
|
16
|
+
}
|
|
6
17
|
export function redactSecrets(s) {
|
|
7
18
|
return scrubSecrets(s)
|
|
8
|
-
.replace(CREDENTIALS_IN_AUTHORITY,
|
|
19
|
+
.replace(CREDENTIALS_IN_AUTHORITY, (_m, scheme, userinfo) => redactUserinfo(scheme, userinfo))
|
|
9
20
|
.replace(/([?&#](?:sig|signature|sas|code)=)[^&\s"'#]+/gi, "$1[redacted]");
|
|
10
21
|
}
|
|
11
22
|
export function redactHostLeaks(s) {
|
|
@@ -13,6 +24,22 @@ export function redactHostLeaks(s) {
|
|
|
13
24
|
.replace(URL_OF_ANY_PROTOCOL, "[redacted-url]")
|
|
14
25
|
.replace(/(?:\/[A-Za-z0-9_.-]+){2,}/g, "[redacted-path]");
|
|
15
26
|
}
|
|
27
|
+
const REDACTION_MARKER_RE = /\[redacted(?:-[a-z]+)?\]/g;
|
|
28
|
+
function countRedactionMarkers(s) {
|
|
29
|
+
return (s.match(REDACTION_MARKER_RE) ?? []).length;
|
|
30
|
+
}
|
|
31
|
+
function safeEllipsisTruncate(s, max) {
|
|
32
|
+
if (max < 1)
|
|
33
|
+
return "";
|
|
34
|
+
const budget = max - 1;
|
|
35
|
+
let out = "";
|
|
36
|
+
for (const codePoint of s) {
|
|
37
|
+
if (out.length + codePoint.length > budget)
|
|
38
|
+
break;
|
|
39
|
+
out += codePoint;
|
|
40
|
+
}
|
|
41
|
+
return out + "…";
|
|
42
|
+
}
|
|
16
43
|
function boundedString(value, max, redact) {
|
|
17
44
|
let s;
|
|
18
45
|
try {
|
|
@@ -24,18 +51,32 @@ function boundedString(value, max, redact) {
|
|
|
24
51
|
s = redact(s);
|
|
25
52
|
if (s.length <= max)
|
|
26
53
|
return s;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
const totalMarkers = countRedactionMarkers(s);
|
|
55
|
+
const plainSuffix = (keepN) => `…[+${s.length - keepN} chars]`;
|
|
56
|
+
const enrichedSuffix = (keepN) => {
|
|
57
|
+
const lost = totalMarkers - countRedactionMarkers(s.slice(0, keepN));
|
|
58
|
+
return lost > 0 ? `…[+${s.length - keepN} chars, ${lost} redaction marker(s) among them]` : plainSuffix(keepN);
|
|
59
|
+
};
|
|
60
|
+
const converge = (suffixFor) => {
|
|
61
|
+
let keep = max;
|
|
62
|
+
let out = "";
|
|
63
|
+
for (let i = 0; i < 4; i++) {
|
|
64
|
+
out = `${s.slice(0, keep)}${suffixFor(keep)}`;
|
|
65
|
+
if (out.length <= max)
|
|
66
|
+
return out;
|
|
67
|
+
if (keep === 0)
|
|
68
|
+
return undefined;
|
|
69
|
+
keep = Math.max(0, keep - (out.length - max));
|
|
35
70
|
}
|
|
36
|
-
|
|
71
|
+
return out.length <= max ? out : undefined;
|
|
72
|
+
};
|
|
73
|
+
const out = (totalMarkers > 0 ? converge(enrichedSuffix) : undefined) ?? converge(plainSuffix);
|
|
74
|
+
if (out === undefined) {
|
|
75
|
+
return safeEllipsisTruncate(s, max);
|
|
37
76
|
}
|
|
38
|
-
|
|
77
|
+
if (out.length >= s.length)
|
|
78
|
+
return s;
|
|
79
|
+
return out;
|
|
39
80
|
}
|
|
40
81
|
export function boundedRedactedSummary(value, max) {
|
|
41
82
|
return boundedString(value, max, redactSecrets);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.448.1",
|
|
4
4
|
"description": "Stateless, task-oriented AI agent core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -62,6 +62,9 @@
|
|
|
62
62
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
63
63
|
"typebox": "1.1.39"
|
|
64
64
|
},
|
|
65
|
+
"optionalDependencies": {
|
|
66
|
+
"sharp": "^0.35.0"
|
|
67
|
+
},
|
|
65
68
|
"devDependencies": {
|
|
66
69
|
"@types/node": "22.10.2",
|
|
67
70
|
"@types/pg": "^8.20.0",
|