@sema-agent/server 1.293.0 → 1.294.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.
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { hostShell, resolveHostShell } from "../plugins/host-platform.js";
|
|
2
3
|
import { HooksConfig, DEFAULT_HOOK_TIMEOUT_SECONDS, HOOK_EVENT_OWNER, } from "@sema-agent/registry-core/hooks";
|
|
3
4
|
import { redactSecrets } from "../trace/redact.js";
|
|
4
5
|
import { ccPromptSystemFor, wrapCondition, parseCcVerdict, CC_EVALUATOR_MAX_OUTPUT_TOKENS } from "./cc-stop-prompt.js";
|
|
5
6
|
import { renderBranchTranscript } from "./branch-transcript.js";
|
|
7
|
+
void resolveHostShell().catch(() => undefined);
|
|
6
8
|
export const MAX_HOOK_ENTRIES_PER_EVENT = 32;
|
|
7
9
|
export const MAX_HOOK_COMMAND_CHARS = 8_192;
|
|
8
10
|
export const MAX_HOOK_TIMEOUT_SECONDS = 600;
|
|
@@ -144,7 +146,8 @@ function runCommandHook(entry, payload, ctx) {
|
|
|
144
146
|
const timeoutMs = Math.min(entry.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS, MAX_HOOK_TIMEOUT_SECONDS) * 1000;
|
|
145
147
|
let child;
|
|
146
148
|
try {
|
|
147
|
-
|
|
149
|
+
const sh = hostShell();
|
|
150
|
+
child = spawn(sh.shell, [...sh.args, entry.command], {
|
|
148
151
|
cwd: ctx.cwd,
|
|
149
152
|
env: hookEnv(ctx),
|
|
150
153
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -203,7 +206,8 @@ function fireAsyncCommandHook(entry, payload, ctx, event) {
|
|
|
203
206
|
try {
|
|
204
207
|
const rewake = entry.asyncRewake === true;
|
|
205
208
|
const timeoutMs = Math.min(entry.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS, MAX_HOOK_TIMEOUT_SECONDS) * 1000;
|
|
206
|
-
const
|
|
209
|
+
const sh = hostShell();
|
|
210
|
+
const child = spawn(sh.shell, [...sh.args, entry.command], {
|
|
207
211
|
cwd: ctx.cwd,
|
|
208
212
|
env: hookEnv(ctx),
|
|
209
213
|
stdio: ["pipe", "ignore", rewake ? "pipe" : "ignore"],
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { getShellConfig, killProcessTree, signalProcessTree } from "@sema-agent/core";
|
|
2
2
|
export const IS_WIN32 = process.platform === "win32";
|
|
3
|
-
const
|
|
3
|
+
const POSIX_SH_FALLBACK = { shell: "/bin/sh", args: ["-c"] };
|
|
4
4
|
let cachedWinShell;
|
|
5
|
+
let cachedPosixShell;
|
|
5
6
|
export async function resolveHostShell() {
|
|
6
|
-
if (!IS_WIN32)
|
|
7
|
-
|
|
7
|
+
if (!IS_WIN32) {
|
|
8
|
+
if (cachedPosixShell)
|
|
9
|
+
return cachedPosixShell;
|
|
10
|
+
const posixCfg = await getShellConfig(process.env.HOST_SHELL_PATH || undefined);
|
|
11
|
+
cachedPosixShell = posixCfg.ok ? { shell: posixCfg.value.shell, args: posixCfg.value.args } : POSIX_SH_FALLBACK;
|
|
12
|
+
return cachedPosixShell;
|
|
13
|
+
}
|
|
8
14
|
if (cachedWinShell)
|
|
9
15
|
return cachedWinShell;
|
|
10
16
|
const custom = process.env.HOST_SHELL_PATH;
|
|
@@ -18,14 +24,16 @@ export async function resolveHostShell() {
|
|
|
18
24
|
return cachedWinShell;
|
|
19
25
|
}
|
|
20
26
|
export function hostShell() {
|
|
21
|
-
if (!IS_WIN32)
|
|
22
|
-
return
|
|
27
|
+
if (!IS_WIN32) {
|
|
28
|
+
return cachedPosixShell ?? POSIX_SH_FALLBACK;
|
|
29
|
+
}
|
|
23
30
|
if (!cachedWinShell)
|
|
24
31
|
throw new Error("host shell not resolved yet — resolveHostShell() must settle before any spawn (shellReady ordering bug)");
|
|
25
32
|
return cachedWinShell;
|
|
26
33
|
}
|
|
27
34
|
export function resetHostShellCacheForTest() {
|
|
28
35
|
cachedWinShell = undefined;
|
|
36
|
+
cachedPosixShell = undefined;
|
|
29
37
|
}
|
|
30
38
|
export function spawnGroupOptions() {
|
|
31
39
|
return IS_WIN32 ? { detached: false, windowsHide: true } : { detached: true };
|
|
@@ -114,6 +114,9 @@ export declare class RemoteLocalDockerExecutionEnv implements RemoteExecutionEnv
|
|
|
114
114
|
private execArgv;
|
|
115
115
|
private execRaw;
|
|
116
116
|
private docker;
|
|
117
|
+
private containerShell;
|
|
118
|
+
private containerShellProbed;
|
|
119
|
+
private probeContainerShell;
|
|
117
120
|
private ensureConnectedExec;
|
|
118
121
|
private fsReady;
|
|
119
122
|
private tempResult;
|
|
@@ -480,7 +480,7 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
480
480
|
const mergedEnv = { ...(this.cfg.env ?? {}), ...(env ?? {}) };
|
|
481
481
|
for (const [k, v] of Object.entries(mergedEnv))
|
|
482
482
|
args.push("-e", `${k}=${v}`);
|
|
483
|
-
args.push(this.containerName,
|
|
483
|
+
args.push(this.containerName, this.containerShell, "-c", command);
|
|
484
484
|
return args;
|
|
485
485
|
}
|
|
486
486
|
execRaw(command) {
|
|
@@ -547,14 +547,31 @@ export class RemoteLocalDockerExecutionEnv {
|
|
|
547
547
|
});
|
|
548
548
|
});
|
|
549
549
|
}
|
|
550
|
+
containerShell = "/bin/sh";
|
|
551
|
+
containerShellProbed = false;
|
|
552
|
+
async probeContainerShell() {
|
|
553
|
+
if (this.containerShellProbed)
|
|
554
|
+
return;
|
|
555
|
+
this.containerShellProbed = true;
|
|
556
|
+
try {
|
|
557
|
+
const r = await this.docker(this.withHostFlag(["exec", this.containerName, "/bin/sh", "-c", "command -v bash"]), { timeoutMs: this.cfg.controlTimeoutMs });
|
|
558
|
+
if (r.ok && r.value.exitCode === 0 && r.value.stdout.trim().length > 0)
|
|
559
|
+
this.containerShell = "bash";
|
|
560
|
+
}
|
|
561
|
+
catch {
|
|
562
|
+
}
|
|
563
|
+
}
|
|
550
564
|
async ensureConnectedExec() {
|
|
551
565
|
if (this.destroyed)
|
|
552
566
|
return { ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") };
|
|
553
|
-
if (this.containerId)
|
|
567
|
+
if (this.containerId) {
|
|
568
|
+
await this.probeContainerShell();
|
|
554
569
|
return ok(undefined);
|
|
570
|
+
}
|
|
555
571
|
const c = await this.connect();
|
|
556
572
|
if (!c.ok)
|
|
557
573
|
return { ok: false, error: new ExecutionError("shell_unavailable", `local-docker connect failed: ${c.error.message}`, c.error) };
|
|
574
|
+
await this.probeContainerShell();
|
|
558
575
|
return ok(undefined);
|
|
559
576
|
}
|
|
560
577
|
async fsReady(p) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.294.0",
|
|
4
4
|
"description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|