@sema-agent/server 1.285.1 → 1.286.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.
package/README.md CHANGED
@@ -82,10 +82,15 @@ Requirements: Node ≥ 20 (npm path) and an OpenAI-compatible model gateway.
82
82
  > noise with no runtime exposure here**: `glob` has exactly one load site inside `e2b` — a `dynamicImport`
83
83
  > in its *template-build* file-packing path — and this server only ever touches E2B's *sandbox runtime*
84
84
  > API. Verified by execution, not by reading: importing `e2b`, constructing the adapter and driving a real
85
- > `exec` never puts `glob` in the module cache. We cannot silence it for you npm `overrides` only apply
86
- > when the package.json being read *is the project npm was invoked on*, so ours is ignored when this
87
- > package is installed as a dependency. If the warning bothers you, add `"overrides": { "glob": "^13" }`
88
- > to **your own** project's package.json (that is the one npm reads); the real fix is upstream in `e2b`.
85
+ > `exec` never puts `glob` in the module cache. **We deliberately do not silence it.** The only thing that
86
+ > actually works is bundling e2b's whole subtree into this package (measured: the warning does go away)
87
+ > and that costs 2.7 MB → 20.8 MB unpacked, marks the tree `invalid` in `npm ls`, and decouples the `e2b`
88
+ > you install from the one e2b publishes. Not worth it for a warning with no runtime reach. Things that
89
+ > do **not** work, in case you were about to try: our `overrides` (they only apply when the package.json
90
+ > being read *is the project npm was invoked on*), and a published `npm-shrinkwrap.json` (also ignored when
91
+ > this package is a dependency — both measured). If the warning bothers you, add
92
+ > `"overrides": { "glob": "^13" }` to **your own** project's package.json — that is the one npm reads, and
93
+ > `glob@13` is API-compatible with what e2b uses. The real fix is upstream in `e2b`.
89
94
 
90
95
  ```bash
91
96
  # A) npm
package/README.zh-CN.md CHANGED
@@ -74,6 +74,18 @@
74
74
 
75
75
  环境要求:Node ≥ 20(npm 路径)+ 一个 OpenAI 兼容模型网关。
76
76
 
77
+ > **关于安装时那条 `glob@11` 弃用警告。** `npm install` 会为 `glob@11.1.0` 打一条 deprecated 警告,它由
78
+ > `e2b`(E2B 沙箱 SDK)间接引入。这是**安装期噪声,在这里没有任何运行时曝露面**:`glob` 在 `e2b` 里的
79
+ > 唯一加载点是它 *template-build* 打包路径里的一处 `dynamicImport`,而本服务只使用 E2B 的**沙箱运行时**
80
+ > 接口。这一条是**跑出来的、不是读代码推断的**:import `e2b`、构造适配器、真调一次 `exec`,全程 `glob`
81
+ > 从未进入模块缓存。**我们是刻意不消它的。** 唯一真能消掉的办法是把 e2b 整棵子树 bundle 进本包
82
+ > (实测有效,警告确实消失),代价是解包体积 2.7 MB → 20.8 MB、`npm ls` 会把依赖树标成 `invalid`、
83
+ > 且你装到的 `e2b` 与 e2b 官方发布的那份脱钩 —— 为一条没有运行时影响的警告付这些,不划算。
84
+ > **顺带告诉你哪些做法没用**(免得你去试):我们写的 `overrides`(只在"被读的那份 package.json 正是
85
+ > npm 本次调用的项目清单"时生效),以及随包发布的 `npm-shrinkwrap.json`(本包作为依赖被安装时同样被
86
+ > 忽略)—— 两条都是实测。若这条警告碍事,把 `"overrides": { "glob": "^13" }` 加进**你自己项目**的
87
+ > package.json(那份才是 npm 会读的;`glob@13` 与 e2b 用到的 API 兼容,已验)。真正的修复在上游 `e2b`。
88
+
77
89
  ```bash
78
90
  # A) npm
79
91
  npm install @sema-agent/server
@@ -6,6 +6,8 @@ export interface HardenedVmLimits {
6
6
  concurrency?: number;
7
7
  maxHeapMb?: number;
8
8
  }
9
+ export declare function setScriptRealmRejectionObserver(observer: (summary: string) => void): void;
10
+ export declare function installScriptRealmRejectionGuard(): void;
9
11
  export interface HardenedMembrane {
10
12
  bridge(hostFn: (...args: unknown[]) => unknown): (...a: unknown[]) => Promise<unknown>;
11
13
  dataIn(value: unknown): unknown;
@@ -24,6 +24,30 @@ function scrubProtoDeep(value) {
24
24
  scrubProtoDeep(value[key]);
25
25
  }
26
26
  }
27
+ let rejectionGuardInstalled = false;
28
+ let onScriptRealmRejection;
29
+ export function setScriptRealmRejectionObserver(observer) {
30
+ onScriptRealmRejection = observer;
31
+ }
32
+ export function installScriptRealmRejectionGuard() {
33
+ if (rejectionGuardInstalled)
34
+ return;
35
+ rejectionGuardInstalled = true;
36
+ process.on("unhandledRejection", (reason, promise) => {
37
+ if (!(promise instanceof Promise)) {
38
+ const summary = reason instanceof Error ? `${reason.name}: ${reason.message}` : String(reason);
39
+ try {
40
+ (onScriptRealmRejection ?? ((s) => console.warn(`hardened-vm: un-awaited script rejection contained: ${s}`)))(summary);
41
+ }
42
+ catch {
43
+ }
44
+ return;
45
+ }
46
+ if (process.listenerCount("unhandledRejection") > 1)
47
+ return;
48
+ throw reason;
49
+ });
50
+ }
27
51
  function sterilizingParse(json) {
28
52
  const parsed = JSON.parse(json);
29
53
  scrubProtoDeep(parsed);
@@ -44,6 +68,7 @@ async function runHardened(opts) {
44
68
  const { signal } = opts;
45
69
  if (signal?.aborted)
46
70
  throw new WorkflowScriptError("hardened-vm aborted before start");
71
+ installScriptRealmRejectionGuard();
47
72
  const sandbox = { __proto__: null };
48
73
  const ctx = vm.createContext(sandbox, { codeGeneration: { strings: false, wasm: false } });
49
74
  vm.runInContext(`(() => {
@@ -73,7 +98,20 @@ async function runHardened(opts) {
73
98
  if (r.ok) return r.v;
74
99
  const e = new Error(r.err); e.name = r.name || "Error"; throw e;
75
100
  })`, ctx);
101
+ let inFlightHostCalls = 0;
102
+ let scriptSettled = false;
103
+ let fireDeadlock;
104
+ const probeQuiescence = () => {
105
+ if (scriptSettled || inFlightHostCalls > 0)
106
+ return;
107
+ setImmediate(() => {
108
+ if (scriptSettled || inFlightHostCalls > 0)
109
+ return;
110
+ fireDeadlock?.();
111
+ });
112
+ };
76
113
  const bridge = (hostFn) => mkBridge(async (argsJson) => {
114
+ inFlightHostCalls++;
77
115
  try {
78
116
  const out = await hostFn(...sterilizingParse(argsJson));
79
117
  return JSON.stringify({ ok: true, v: out ?? null });
@@ -85,6 +123,10 @@ async function runHardened(opts) {
85
123
  name: e instanceof Error ? e.name : "Error",
86
124
  });
87
125
  }
126
+ finally {
127
+ inFlightHostCalls--;
128
+ probeQuiescence();
129
+ }
88
130
  });
89
131
  const membrane = { bridge, dataIn, ctxEval: (src) => vm.runInContext(src, ctx) };
90
132
  for (const [name, value] of Object.entries(opts.buildGlobals(membrane)))
@@ -97,6 +139,8 @@ async function runHardened(opts) {
97
139
  throw new WorkflowScriptError(`script failed to compile: ${err instanceof Error ? err.message : String(err)}`);
98
140
  }
99
141
  const scriptPromise = invocation.runInContext(ctx, { timeout: cfg.syncTimeoutMs });
142
+ scriptPromise.then(() => (scriptSettled = true), () => (scriptSettled = true));
143
+ probeQuiescence();
100
144
  let timer;
101
145
  let onAbort;
102
146
  try {
@@ -109,6 +153,7 @@ async function runHardened(opts) {
109
153
  onAbort = () => reject(new WorkflowScriptError("hardened-vm aborted"));
110
154
  signal.addEventListener("abort", onAbort, { once: true });
111
155
  }
156
+ fireDeadlock = () => reject(new WorkflowScriptError("script deadlocked: no host call (agent/phase) in flight and nothing can resume the script — usually an un-awaited promise cycle, e.g. `const r = phase(...)` without await whose body reads `r`."));
112
157
  }),
113
158
  ]);
114
159
  return marshalOut(result);
@@ -268,7 +268,7 @@ export async function emitPendingWorkflowCompletions(inbox, sessionId, callerPri
268
268
  }
269
269
  catch {
270
270
  }
271
- await emit({ type: "task_notification", task_id: e.runId, status: e.status, summary: e.summary, ...extras });
271
+ await emit({ type: "task_notification", task_id: e.runId, status: e.status, summary: e.summary, ...extras, injected: false });
272
272
  }
273
273
  else {
274
274
  await emit({ type: "workflow_complete", runId: e.runId, status: e.status, summary: e.summary });
@@ -82,6 +82,7 @@ export function taskProgressEventData(ev) {
82
82
  export function taskNotificationEventData(ev) {
83
83
  const n = ev.notification;
84
84
  return {
85
+ injected: false,
85
86
  task_id: n.task_id,
86
87
  task_type: n.task_type,
87
88
  ...(n.sessionId !== undefined ? { sessionId: n.sessionId } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "1.285.1",
3
+ "version": "1.286.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",