@sweny-ai/core 0.1.152 → 0.1.153
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/agent-env.d.ts +53 -0
- package/dist/agent-env.js +128 -2
- package/dist/executor.js +6 -1
- package/dist/harness/acp.js +2 -2
- package/dist/harness/claude-code.js +3 -3
- package/dist/harness/codex.js +2 -2
- package/dist/harness/pi.js +2 -2
- package/dist/harness/types.d.ts +7 -0
- package/package.json +1 -1
package/dist/agent-env.d.ts
CHANGED
|
@@ -150,10 +150,63 @@ export declare function scopeAgentEnv(source: Record<string, string | undefined>
|
|
|
150
150
|
* values are always dropped. Pure: never mutates `source`.
|
|
151
151
|
*/
|
|
152
152
|
export declare function buildAgentEnv(source: Record<string, string | undefined>, opts?: BuildAgentEnvOpts): Record<string, string>;
|
|
153
|
+
/**
|
|
154
|
+
* Write tokens withheld from the agent when pushes are blocked. `gh`, the
|
|
155
|
+
* GitHub/GitLab REST APIs and git's HTTPS auth all read these; skill tools
|
|
156
|
+
* run in the sweny process, not the agent, so reads through skills still work.
|
|
157
|
+
* The harness's own model credentials are never in this list.
|
|
158
|
+
*/
|
|
159
|
+
export declare const PUSH_TOKEN_VARS: readonly string[];
|
|
160
|
+
/** The remote name every push is pointed at under {@link withPushBlocked}. It never exists. */
|
|
161
|
+
export declare const NO_PUSH_REMOTE = "sweny-no-push";
|
|
162
|
+
/**
|
|
163
|
+
* A sweny-owned directory holding a `pre-push` hook and a `GIT_ASKPASS`
|
|
164
|
+
* program that both refuse, an ssh wrapper that refuses pushes, and an empty
|
|
165
|
+
* `gh` config dir. Created once per process under the OS temp dir.
|
|
166
|
+
*/
|
|
167
|
+
export declare function noPushDir(): string;
|
|
168
|
+
/**
|
|
169
|
+
* Git config entries (command scope, via `GIT_CONFIG_COUNT`) that make every
|
|
170
|
+
* push fail while leaving fetch, commit and diff alone:
|
|
171
|
+
*
|
|
172
|
+
* - `credential.helper` set to empty resets the helper list, so no stored
|
|
173
|
+
* credential (keychain, `gh auth git-credential`, store) reaches git;
|
|
174
|
+
* - `remote.pushDefault` names a remote that does not exist and
|
|
175
|
+
* `push.default=nothing` refuses a bare `git push`;
|
|
176
|
+
* - `url.<unusable>.pushInsteadOf` rewrites every https/ssh/git/file/absolute
|
|
177
|
+
* path push URL to a scheme git has no helper for;
|
|
178
|
+
* - `core.hooksPath` points at a sweny `pre-push` hook that exits 1, which
|
|
179
|
+
* also covers a remote with an explicit `pushurl` (git ignores
|
|
180
|
+
* pushInsteadOf there). Repo hooks do not run in these nodes.
|
|
181
|
+
*/
|
|
182
|
+
export declare function noPushGitConfig(dir: string): Array<[string, string]>;
|
|
183
|
+
/**
|
|
184
|
+
* The agent env with pushes blocked (#442). Applied to every node's agent
|
|
185
|
+
* env when a run is staged or a dry run, on every harness, whether or not env
|
|
186
|
+
* scoping is on. Withholds {@link PUSH_TOKEN_VARS}, appends
|
|
187
|
+
* {@link noPushGitConfig} after any `GIT_CONFIG_*` entries already present,
|
|
188
|
+
* sets `GIT_ASKPASS` to a refusing program, turns off git's terminal prompt,
|
|
189
|
+
* routes git's ssh through a wrapper that refuses `git-receive-pack` (so an
|
|
190
|
+
* ssh push fails even to a remote with an explicit `pushurl` under
|
|
191
|
+
* `--no-verify`; ssh fetches still run the operator's own ssh command), and
|
|
192
|
+
* gives `gh` an empty config dir so a stored `gh auth login` is not used.
|
|
193
|
+
*
|
|
194
|
+
* Not a sandbox: a process that rewrites its own env or git config and
|
|
195
|
+
* finds a credential on disk (an ssh key without a passphrase, a keychain
|
|
196
|
+
* entry) is outside what env can stop. The sandbox wrapper hides credential
|
|
197
|
+
* files such as `~/.ssh`. `enabled` false returns `env` unchanged. Pure apart from
|
|
198
|
+
* creating {@link noPushDir} once.
|
|
199
|
+
*/
|
|
200
|
+
export declare function withPushBlocked(env: Record<string, string>, enabled: boolean | undefined): Record<string, string>;
|
|
153
201
|
/** What one node's agent call may see: env var names and network hosts. */
|
|
154
202
|
export interface AgentAccess {
|
|
155
203
|
envVars: string[];
|
|
156
204
|
domains: string[];
|
|
205
|
+
/**
|
|
206
|
+
* The run is staged or a dry run (#442): the harness applies
|
|
207
|
+
* {@link withPushBlocked} to the agent env, so no push can leave the node.
|
|
208
|
+
*/
|
|
209
|
+
noPush?: boolean;
|
|
157
210
|
}
|
|
158
211
|
/** Hosts reachable by sandboxed commands in every node: source hosting + package registries. */
|
|
159
212
|
export declare const DEFAULT_SANDBOX_DOMAINS: readonly string[];
|
package/dist/agent-env.js
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* SWENY_SANDBOX_ALLOWED_DOMAINS / sandbox-allowed-domains extra hosts for sandboxed commands
|
|
23
23
|
*/
|
|
24
24
|
import { execFileSync } from "node:child_process";
|
|
25
|
-
import { existsSync } from "node:fs";
|
|
26
|
-
import { homedir } from "node:os";
|
|
25
|
+
import { existsSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
26
|
+
import { homedir, tmpdir } from "node:os";
|
|
27
27
|
import * as path from "node:path";
|
|
28
28
|
// ─── Scoped env ──────────────────────────────────────────────────
|
|
29
29
|
/**
|
|
@@ -429,6 +429,132 @@ export function buildAgentEnv(source, opts = {}) {
|
|
|
429
429
|
}
|
|
430
430
|
return out;
|
|
431
431
|
}
|
|
432
|
+
// ─── No push under --stage / --dry-run (#442) ────────────────────
|
|
433
|
+
/**
|
|
434
|
+
* Write tokens withheld from the agent when pushes are blocked. `gh`, the
|
|
435
|
+
* GitHub/GitLab REST APIs and git's HTTPS auth all read these; skill tools
|
|
436
|
+
* run in the sweny process, not the agent, so reads through skills still work.
|
|
437
|
+
* The harness's own model credentials are never in this list.
|
|
438
|
+
*/
|
|
439
|
+
export const PUSH_TOKEN_VARS = [
|
|
440
|
+
"GITHUB_TOKEN",
|
|
441
|
+
"GH_TOKEN",
|
|
442
|
+
"GH_ENTERPRISE_TOKEN",
|
|
443
|
+
"GITHUB_ENTERPRISE_TOKEN",
|
|
444
|
+
"GITHUB_PAT",
|
|
445
|
+
"GITLAB_TOKEN",
|
|
446
|
+
"GL_TOKEN",
|
|
447
|
+
"CI_JOB_TOKEN",
|
|
448
|
+
"BITBUCKET_TOKEN",
|
|
449
|
+
// git transport credentials: the ssh agent socket and any askpass program
|
|
450
|
+
"SSH_AUTH_SOCK",
|
|
451
|
+
"SSH_ASKPASS",
|
|
452
|
+
"GIT_ASKPASS",
|
|
453
|
+
];
|
|
454
|
+
/** The remote name every push is pointed at under {@link withPushBlocked}. It never exists. */
|
|
455
|
+
export const NO_PUSH_REMOTE = "sweny-no-push";
|
|
456
|
+
/** URL prefixes rewritten to an unusable scheme for pushes only (fetches are untouched). */
|
|
457
|
+
const PUSH_URL_PREFIXES = ["https://", "http://", "ssh://", "git://", "git@", "file://", "/"];
|
|
458
|
+
const NO_PUSH_MESSAGE = "sweny: git push is blocked under --stage and --dry-run (#442)";
|
|
459
|
+
let noPushDirCache;
|
|
460
|
+
/** Single-quote for sh: git runs `GIT_SSH_COMMAND` through the shell. */
|
|
461
|
+
function shQuote(s) {
|
|
462
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* A sweny-owned directory holding a `pre-push` hook and a `GIT_ASKPASS`
|
|
466
|
+
* program that both refuse, an ssh wrapper that refuses pushes, and an empty
|
|
467
|
+
* `gh` config dir. Created once per process under the OS temp dir.
|
|
468
|
+
*/
|
|
469
|
+
export function noPushDir() {
|
|
470
|
+
if (noPushDirCache && existsSync(path.join(noPushDirCache, "hooks", "pre-push")))
|
|
471
|
+
return noPushDirCache;
|
|
472
|
+
const dir = mkdtempSync(path.join(tmpdir(), "sweny-no-push-"));
|
|
473
|
+
mkdirSync(path.join(dir, "hooks"));
|
|
474
|
+
mkdirSync(path.join(dir, "gh"));
|
|
475
|
+
const refuse = `#!/bin/sh\necho "${NO_PUSH_MESSAGE}" >&2\nexit 1\n`;
|
|
476
|
+
writeFileSync(path.join(dir, "hooks", "pre-push"), refuse, { mode: 0o755 });
|
|
477
|
+
writeFileSync(path.join(dir, "askpass"), refuse, { mode: 0o755 });
|
|
478
|
+
// git runs `<ssh> host "git-receive-pack '<path>'"` to push and
|
|
479
|
+
// git-upload-pack to fetch: refuse the first, hand everything else to the
|
|
480
|
+
// operator's own ssh command (or plain ssh).
|
|
481
|
+
const ssh = `#!/bin/sh\n` +
|
|
482
|
+
`for a in "$@"; do case "$a" in *receive-pack*) echo "${NO_PUSH_MESSAGE}" >&2; exit 1;; esac; done\n` +
|
|
483
|
+
`if [ -n "$SWENY_NO_PUSH_SSH" ]; then exec sh -c "$SWENY_NO_PUSH_SSH \\"\\$@\\"" ssh "$@"; fi\n` +
|
|
484
|
+
`exec ssh "$@"\n`;
|
|
485
|
+
writeFileSync(path.join(dir, "ssh"), ssh, { mode: 0o755 });
|
|
486
|
+
noPushDirCache = dir;
|
|
487
|
+
return dir;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Git config entries (command scope, via `GIT_CONFIG_COUNT`) that make every
|
|
491
|
+
* push fail while leaving fetch, commit and diff alone:
|
|
492
|
+
*
|
|
493
|
+
* - `credential.helper` set to empty resets the helper list, so no stored
|
|
494
|
+
* credential (keychain, `gh auth git-credential`, store) reaches git;
|
|
495
|
+
* - `remote.pushDefault` names a remote that does not exist and
|
|
496
|
+
* `push.default=nothing` refuses a bare `git push`;
|
|
497
|
+
* - `url.<unusable>.pushInsteadOf` rewrites every https/ssh/git/file/absolute
|
|
498
|
+
* path push URL to a scheme git has no helper for;
|
|
499
|
+
* - `core.hooksPath` points at a sweny `pre-push` hook that exits 1, which
|
|
500
|
+
* also covers a remote with an explicit `pushurl` (git ignores
|
|
501
|
+
* pushInsteadOf there). Repo hooks do not run in these nodes.
|
|
502
|
+
*/
|
|
503
|
+
export function noPushGitConfig(dir) {
|
|
504
|
+
return [
|
|
505
|
+
["credential.helper", ""],
|
|
506
|
+
["remote.pushDefault", NO_PUSH_REMOTE],
|
|
507
|
+
["push.default", "nothing"],
|
|
508
|
+
["core.hooksPath", path.join(dir, "hooks")],
|
|
509
|
+
...PUSH_URL_PREFIXES.map((p) => [`url.${NO_PUSH_REMOTE}://blocked/.pushInsteadOf`, p]),
|
|
510
|
+
];
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* The agent env with pushes blocked (#442). Applied to every node's agent
|
|
514
|
+
* env when a run is staged or a dry run, on every harness, whether or not env
|
|
515
|
+
* scoping is on. Withholds {@link PUSH_TOKEN_VARS}, appends
|
|
516
|
+
* {@link noPushGitConfig} after any `GIT_CONFIG_*` entries already present,
|
|
517
|
+
* sets `GIT_ASKPASS` to a refusing program, turns off git's terminal prompt,
|
|
518
|
+
* routes git's ssh through a wrapper that refuses `git-receive-pack` (so an
|
|
519
|
+
* ssh push fails even to a remote with an explicit `pushurl` under
|
|
520
|
+
* `--no-verify`; ssh fetches still run the operator's own ssh command), and
|
|
521
|
+
* gives `gh` an empty config dir so a stored `gh auth login` is not used.
|
|
522
|
+
*
|
|
523
|
+
* Not a sandbox: a process that rewrites its own env or git config and
|
|
524
|
+
* finds a credential on disk (an ssh key without a passphrase, a keychain
|
|
525
|
+
* entry) is outside what env can stop. The sandbox wrapper hides credential
|
|
526
|
+
* files such as `~/.ssh`. `enabled` false returns `env` unchanged. Pure apart from
|
|
527
|
+
* creating {@link noPushDir} once.
|
|
528
|
+
*/
|
|
529
|
+
export function withPushBlocked(env, enabled) {
|
|
530
|
+
if (!enabled)
|
|
531
|
+
return env;
|
|
532
|
+
const dir = noPushDir();
|
|
533
|
+
const out = {};
|
|
534
|
+
for (const [k, v] of Object.entries(env)) {
|
|
535
|
+
if (PUSH_TOKEN_VARS.includes(k))
|
|
536
|
+
continue;
|
|
537
|
+
out[k] = v;
|
|
538
|
+
}
|
|
539
|
+
const base = Number.parseInt(out.GIT_CONFIG_COUNT ?? "0", 10);
|
|
540
|
+
let n = Number.isFinite(base) && base > 0 ? base : 0;
|
|
541
|
+
for (const [key, value] of noPushGitConfig(dir)) {
|
|
542
|
+
out[`GIT_CONFIG_KEY_${n}`] = key;
|
|
543
|
+
out[`GIT_CONFIG_VALUE_${n}`] = value;
|
|
544
|
+
n++;
|
|
545
|
+
}
|
|
546
|
+
out.GIT_CONFIG_COUNT = String(n);
|
|
547
|
+
const ownSsh = env.GIT_SSH_COMMAND ?? (env.GIT_SSH ? shQuote(env.GIT_SSH) : undefined);
|
|
548
|
+
if (ownSsh)
|
|
549
|
+
out.SWENY_NO_PUSH_SSH = ownSsh;
|
|
550
|
+
out.GIT_SSH_COMMAND = shQuote(path.join(dir, "ssh"));
|
|
551
|
+
out.GIT_SSH_VARIANT = "ssh";
|
|
552
|
+
out.GIT_ASKPASS = path.join(dir, "askpass");
|
|
553
|
+
out.GIT_TERMINAL_PROMPT = "0";
|
|
554
|
+
out.GH_CONFIG_DIR = path.join(dir, "gh");
|
|
555
|
+
out.GH_PROMPT_DISABLED = "1";
|
|
556
|
+
return out;
|
|
557
|
+
}
|
|
432
558
|
/** Hosts reachable by sandboxed commands in every node: source hosting + package registries. */
|
|
433
559
|
export const DEFAULT_SANDBOX_DOMAINS = [
|
|
434
560
|
"github.com",
|
package/dist/executor.js
CHANGED
|
@@ -281,7 +281,12 @@ export async function execute(workflow, input, options) {
|
|
|
281
281
|
// compiles them or reports them in `degraded`.
|
|
282
282
|
const denyClasses = (node.tools?.deny ?? []).filter(isToolClass);
|
|
283
283
|
let degradedLogged = false;
|
|
284
|
-
|
|
284
|
+
// #442: a staged or dry run cannot push. The harness blocks git push and
|
|
285
|
+
// withholds write tokens in this node's agent env (see withPushBlocked).
|
|
286
|
+
const agentAccess = {
|
|
287
|
+
...resolveAgentAccess(node.skills, skills),
|
|
288
|
+
...(stageOutputs ? { noPush: true } : {}),
|
|
289
|
+
};
|
|
285
290
|
// #365: a node or workflow that declares `permissions` or `outputs` gets
|
|
286
291
|
// one portable policy, compiled by each adapter. The gate also runs here so
|
|
287
292
|
// a strict refusal holds on every harness, before any spend. Nodes that
|
package/dist/harness/acp.js
CHANGED
|
@@ -48,7 +48,7 @@ import { spawn } from "node:child_process";
|
|
|
48
48
|
import * as fs from "node:fs";
|
|
49
49
|
import * as path from "node:path";
|
|
50
50
|
import { consoleLogger } from "../types.js";
|
|
51
|
-
import { parseList, reportWithheldEnv, resolveEnvScope, resolveSandboxMode, scopeAgentEnv, } from "../agent-env.js";
|
|
51
|
+
import { parseList, reportWithheldEnv, resolveEnvScope, resolveSandboxMode, scopeAgentEnv, withPushBlocked, } from "../agent-env.js";
|
|
52
52
|
import { ACP_CAPABILITIES } from "./capabilities.js";
|
|
53
53
|
import { policyGate, resolveHarnessPolicy, TOOL_CLASSES } from "./policy.js";
|
|
54
54
|
import { buildNodePrompt } from "./prompts.js";
|
|
@@ -714,7 +714,7 @@ export class AcpHarness {
|
|
|
714
714
|
const readOnly = policy.readOnly;
|
|
715
715
|
// Permission answers: the node's deny list, plus legacy `disallowed_tools` names that map to a class.
|
|
716
716
|
const deny = new Set([...policy.deny, ...translateDenyNames(policy.nativeDeny).classes]);
|
|
717
|
-
const env = this.buildEnv(req.agentAccess?.envVars);
|
|
717
|
+
const env = withPushBlocked(this.buildEnv(req.agentAccess?.envVars), req.agentAccess?.noPush);
|
|
718
718
|
let bridge;
|
|
719
719
|
try {
|
|
720
720
|
// Dry run: external MCP servers cannot be classified per tool, so only
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { query, createSdkMcpServer, tool as sdkTool } from "@anthropic-ai/claude-agent-sdk";
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
13
|
import { consoleLogger } from "../types.js";
|
|
14
|
-
import { reportWithheldEnv, parseList, resolveEnvScope, scopeAgentEnv, resolveAgentSandbox, } from "../agent-env.js";
|
|
14
|
+
import { reportWithheldEnv, parseList, resolveEnvScope, scopeAgentEnv, withPushBlocked, resolveAgentSandbox, } from "../agent-env.js";
|
|
15
15
|
import { policyGate } from "./policy.js";
|
|
16
16
|
import { CLAUDE_CODE_CAPABILITIES } from "./capabilities.js";
|
|
17
17
|
import { ask as coreAsk, evaluate as coreEvaluate, buildEvaluatePrompt, buildNodePrompt } from "./prompts.js";
|
|
@@ -310,7 +310,7 @@ export class ClaudeCodeHarness {
|
|
|
310
310
|
disallowedTools: disallowedTools.length > 0 ? disallowedTools : undefined,
|
|
311
311
|
strictMcp: readOnly || policy.strict || policy.exclusiveMcp === true,
|
|
312
312
|
sandboxMode: policy.sandbox,
|
|
313
|
-
agentAccess: { envVars: req.agentAccess?.envVars ?? [], domains: policy.egress },
|
|
313
|
+
agentAccess: { envVars: req.agentAccess?.envVars ?? [], domains: policy.egress, noPush: req.agentAccess?.noPush },
|
|
314
314
|
});
|
|
315
315
|
return { ...result, harness: this.info(), degraded: gate.degraded };
|
|
316
316
|
}
|
|
@@ -380,7 +380,7 @@ export class ClaudeCodeHarness {
|
|
|
380
380
|
// Build prompt. Context (workflow input such as issues/alerts, plus prior
|
|
381
381
|
// node outputs) is fenced as untrusted data (#360).
|
|
382
382
|
const prompt = buildNodePrompt(instruction, context, outputSchema);
|
|
383
|
-
const env = this.buildEnv(agentAccess?.envVars);
|
|
383
|
+
const env = withPushBlocked(this.buildEnv(agentAccess?.envVars), agentAccess?.noPush);
|
|
384
384
|
let response = "";
|
|
385
385
|
// CC-08: when the SDK populates typed structured output (because we passed
|
|
386
386
|
// an `outputFormat`), prefer it over re-parsing JSON out of the free-text
|
package/dist/harness/codex.js
CHANGED
|
@@ -28,7 +28,7 @@ import * as fs from "node:fs";
|
|
|
28
28
|
import * as os from "node:os";
|
|
29
29
|
import * as path from "node:path";
|
|
30
30
|
import { consoleLogger } from "../types.js";
|
|
31
|
-
import { CODEX_AUTH_VARS, CODEX_ENV_PREFIXES, checkSandboxSupport, parseList, reportWithheldEnv, resolveCodexAuthEnv, resolveEnvScope, resolveSandboxMode, scopeAgentEnv, } from "../agent-env.js";
|
|
31
|
+
import { CODEX_AUTH_VARS, CODEX_ENV_PREFIXES, checkSandboxSupport, parseList, reportWithheldEnv, resolveCodexAuthEnv, resolveEnvScope, resolveSandboxMode, scopeAgentEnv, withPushBlocked, } from "../agent-env.js";
|
|
32
32
|
import { CODEX_CAPABILITIES } from "./capabilities.js";
|
|
33
33
|
import { policyGate, resolveHarnessPolicy } from "./policy.js";
|
|
34
34
|
import { buildNodePrompt } from "./prompts.js";
|
|
@@ -763,7 +763,7 @@ export class CodexHarness {
|
|
|
763
763
|
sandbox = s.mode;
|
|
764
764
|
}
|
|
765
765
|
}
|
|
766
|
-
const env = this.buildEnv(req.agentAccess?.envVars);
|
|
766
|
+
const env = withPushBlocked(this.buildEnv(req.agentAccess?.envVars), req.agentAccess?.noPush);
|
|
767
767
|
if (wrapper && !env.CODEX_API_KEY && !env.CODEX_ACCESS_TOKEN && !this.loginWarned) {
|
|
768
768
|
this.loginWarned = true;
|
|
769
769
|
this.logger.warn("Codex runs inside the sandbox wrapper, where a stored `codex login` is not readable; set CODEX_API_KEY or OPENAI_API_KEY.");
|
package/dist/harness/pi.js
CHANGED
|
@@ -34,7 +34,7 @@ import * as fs from "node:fs";
|
|
|
34
34
|
import * as os from "node:os";
|
|
35
35
|
import * as path from "node:path";
|
|
36
36
|
import { consoleLogger } from "../types.js";
|
|
37
|
-
import { PI_AUTH_VARS, PI_ENV_PREFIXES, parseList, reportWithheldEnv, resolveEnvScope, resolveSandboxMode, scopeAgentEnv, } from "../agent-env.js";
|
|
37
|
+
import { PI_AUTH_VARS, PI_ENV_PREFIXES, parseList, reportWithheldEnv, resolveEnvScope, resolveSandboxMode, scopeAgentEnv, withPushBlocked, } from "../agent-env.js";
|
|
38
38
|
import { PI_CAPABILITIES } from "./capabilities.js";
|
|
39
39
|
import { policyGate, resolveHarnessPolicy } from "./policy.js";
|
|
40
40
|
import { buildNodePrompt } from "./prompts.js";
|
|
@@ -746,7 +746,7 @@ export class PiHarness {
|
|
|
746
746
|
if (readOnly)
|
|
747
747
|
for (const c of ["shell", "write", "edit"])
|
|
748
748
|
deny.add(c);
|
|
749
|
-
const env = this.buildEnv(req.agentAccess?.envVars);
|
|
749
|
+
const env = withPushBlocked(this.buildEnv(req.agentAccess?.envVars), req.agentAccess?.noPush);
|
|
750
750
|
const model = req.model ?? this.model;
|
|
751
751
|
let bridge;
|
|
752
752
|
let agentDir;
|
package/dist/harness/types.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Browser-safe: types only, no runtime imports.
|
|
10
10
|
*/
|
|
11
|
+
import type { AgentAccess } from "../agent-env.js";
|
|
11
12
|
import type { Claude, Logger, NodeResult } from "../types.js";
|
|
12
13
|
export type HarnessId = "claude-code" | "mock" | "codex" | "pi" | "hermes" | "gemini" | `acp:${string}`;
|
|
13
14
|
/** Portable tool vocabulary for node policy. */
|
|
@@ -100,6 +101,12 @@ export interface HarnessRunRequest extends ClaudeRunRequest {
|
|
|
100
101
|
* `readOnly` / `disallowedTools` / `agentAccess` fields.
|
|
101
102
|
*/
|
|
102
103
|
policy?: NodePolicy;
|
|
104
|
+
/**
|
|
105
|
+
* {@link ClaudeRunRequest.agentAccess} plus `noPush` (#442): the executor
|
|
106
|
+
* sets it for a staged or dry run, and every adapter applies
|
|
107
|
+
* `withPushBlocked` to the agent env.
|
|
108
|
+
*/
|
|
109
|
+
agentAccess?: AgentAccess;
|
|
103
110
|
}
|
|
104
111
|
export interface HarnessRunResult extends NodeResult {
|
|
105
112
|
harness: HarnessInfo;
|