@sema-agent/server 1.290.0 → 1.292.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 +1 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.js +16 -2
- package/dist/env-facts.d.ts +5 -0
- package/dist/env-facts.js +35 -1
- package/dist/fleet/fleet-bus.d.ts +33 -1
- package/dist/fleet/fleet-bus.js +48 -4
- package/dist/http/server.d.ts +1 -0
- package/dist/http/server.js +49 -11
- package/dist/main.js +15 -5
- package/dist/runs.d.ts +1 -1
- package/dist/runs.js +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -134,7 +134,7 @@ The server is configured entirely through environment variables. The most import
|
|
|
134
134
|
| `MODEL_ID` | `Qwen3.5-35B` | Default model id |
|
|
135
135
|
| `MODEL_API_KEY` | — | Gateway API key (optional) |
|
|
136
136
|
| `SERVICE_AUTH_TOKEN` | — | Callers must send `Authorization: Bearer <token>` |
|
|
137
|
-
| `DB_BACKEND` | `
|
|
137
|
+
| `DB_BACKEND` | `local`* | `mysql` (any MySQL-protocol DB: MySQL/TiDB/MariaDB; `tidb` alias) / `pg` (PostgreSQL) / `local` (file-backed, no DB) / `memory` (explicit in-memory: nothing survives a restart, durable-runs faces 501). *Bare boot (no DB env at all) defaults to `local` so a single-user machine keeps its runs across restarts; any SQL signal (`SESSION_BACKEND` or `TIDB_/MYSQL_/PG_HOST`) keeps the `mysql` engine default, and `REQUIRE_PRINCIPAL=true` bare boots stay `memory` (the local file store has no tenant isolation — a warning says so). A DEFAULT-derived `local` that cannot create its data root degrades to memory with a warning + the `store_backend_degraded` gauge; an EXPLICIT `DB_BACKEND=local` fails loud instead. Setting `mysql`/`pg` explicitly also switches sessions to durable |
|
|
138
138
|
| `SESSION_BACKEND` | `memory`* | `memory` / `mysql` (durable session center; `tidb` alias) / `auto`. *Defaults to durable when `DB_BACKEND` is explicitly `mysql`/`pg` |
|
|
139
139
|
| `REMOTE_EXEC` | unset | Sandbox execution lane: `host` / `local-docker` / `e2b` / `k8s` / `ssh` / `adb`; unset = in-process stub (with `CONFIG_PROVIDER=local` the default becomes `host`) |
|
|
140
140
|
| `CONFIG_PROVIDER` | unset | Config source: `local` (file-backed `config.d/`, single machine) / `remote` (registry control plane) |
|
package/dist/config.d.ts
CHANGED
|
@@ -141,7 +141,8 @@ export interface ServiceConfig {
|
|
|
141
141
|
database: string;
|
|
142
142
|
connectionLimit?: number;
|
|
143
143
|
};
|
|
144
|
-
dbBackend: "mysql" | "pg" | "local";
|
|
144
|
+
dbBackend: "mysql" | "pg" | "local" | "memory";
|
|
145
|
+
dbBackendExplicit: boolean;
|
|
145
146
|
localDataRoot?: string;
|
|
146
147
|
pg?: {
|
|
147
148
|
host: string;
|
package/dist/config.js
CHANGED
|
@@ -168,9 +168,22 @@ export function loadConfig() {
|
|
|
168
168
|
const modelId = env("MODEL_ID", "Qwen3.5-35B");
|
|
169
169
|
const singleUserTurnkey = process.env.REQUIRE_PRINCIPAL !== "true";
|
|
170
170
|
const postureOn = (envVal, infraReady = true) => envVal === "true" ? true : envVal === "false" ? false : singleUserTurnkey && infraReady;
|
|
171
|
-
const
|
|
171
|
+
const dbBackendSet = !!process.env.DB_BACKEND;
|
|
172
|
+
const sqlSignal = (!!process.env.SESSION_BACKEND && process.env.SESSION_BACKEND !== "memory") ||
|
|
173
|
+
!!(process.env.TIDB_HOST || process.env.MYSQL_HOST || process.env.PG_HOST);
|
|
174
|
+
const bareBoot = !dbBackendSet && !sqlSignal;
|
|
175
|
+
const multiTenantBareBoot = bareBoot && process.env.REQUIRE_PRINCIPAL === "true";
|
|
176
|
+
const dbBackendRaw = enumEnv("DB_BACKEND", bareBoot ? (multiTenantBareBoot ? "memory" : "local") : "mysql", ["mysql", "tidb", "pg", "local", "memory"]);
|
|
172
177
|
const dbBackend = dbBackendRaw === "tidb" ? "mysql" : dbBackendRaw;
|
|
173
|
-
|
|
178
|
+
if (multiTenantBareBoot) {
|
|
179
|
+
CONFIG_NOTICES.push({
|
|
180
|
+
event: "db_backend_default_memory_multi_tenant",
|
|
181
|
+
fields: {
|
|
182
|
+
note: "REQUIRE_PRINCIPAL=true with no DB_BACKEND — the single-user default (local file store) is incompatible with multi-tenant (no tenant isolation in the file owner map), so this deployment runs IN-MEMORY (runs/sessions lost on restart, durable runs 501). Configure DB_BACKEND=mysql|pg for a durable multi-tenant deployment.",
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
const sqlEngineExplicit = !!process.env.DB_BACKEND && dbBackend !== "local" && dbBackend !== "memory";
|
|
174
187
|
const sessionBackendRaw = enumEnv("SESSION_BACKEND", sqlEngineExplicit ? "mysql" : "memory", ["memory", "mysql", "tidb", "auto"]);
|
|
175
188
|
const sessionBackend = sessionBackendRaw === "mysql" ? "tidb" : sessionBackendRaw;
|
|
176
189
|
const memoryEngineEnabled = process.env.MEMORY_ENGINE !== "off";
|
|
@@ -487,6 +500,7 @@ export function loadConfig() {
|
|
|
487
500
|
}
|
|
488
501
|
: undefined,
|
|
489
502
|
dbBackend,
|
|
503
|
+
dbBackendExplicit: dbBackendSet,
|
|
490
504
|
localDataRoot,
|
|
491
505
|
tidb: needsDb && dbBackend === "mysql"
|
|
492
506
|
? {
|
package/dist/env-facts.d.ts
CHANGED
|
@@ -25,6 +25,11 @@ export declare function resumeFactsForLane(provider: "e2b" | "k8s" | "ssh" | "ad
|
|
|
25
25
|
export declare function scratchpadSessionSegment(sessionId: string): string;
|
|
26
26
|
export declare function scratchpadDirFor(localDataRoot: string, sessionId: string): string;
|
|
27
27
|
export declare function ensureScratchpadDir(localDataRoot: string, sessionId: string): Promise<string | undefined>;
|
|
28
|
+
export declare function acceptShellScratchpadDir(raw: unknown, opts: {
|
|
29
|
+
requirePrincipal: boolean;
|
|
30
|
+
hostSemanticsLane: boolean;
|
|
31
|
+
warn: (msg: string, meta?: object) => void;
|
|
32
|
+
}): Promise<string | undefined>;
|
|
28
33
|
export declare function purgeScratchpadDir(localDataRoot: string, sessionId: string): Promise<void>;
|
|
29
34
|
export declare function sweepStaleScratchpads(localDataRoot: string, opts: {
|
|
30
35
|
olderThanMs: number;
|
package/dist/env-facts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
1
|
+
import { isAbsolute, join, resolve, sep } from "node:path";
|
|
2
2
|
import { mkdir, readdir, rm, stat } from "node:fs/promises";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
const MAX_STR = 64;
|
|
@@ -59,6 +59,40 @@ export async function ensureScratchpadDir(localDataRoot, sessionId) {
|
|
|
59
59
|
return undefined;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
export async function acceptShellScratchpadDir(raw, opts) {
|
|
63
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
64
|
+
return undefined;
|
|
65
|
+
if (opts.requirePrincipal) {
|
|
66
|
+
opts.warn("shell_scratchpad_ignored", { reason: "multi_tenant", note: "tenant-supplied exempt dir on a shared worker disk — server-computed scratchpad used instead" });
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
if (!opts.hostSemanticsLane) {
|
|
70
|
+
opts.warn("shell_scratchpad_ignored", { reason: "remote_lane", note: "worker-local path is not real to remote-sandbox hands — in-sandbox scratchpad used instead" });
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
if (!isAbsolute(raw)) {
|
|
74
|
+
opts.warn("shell_scratchpad_ignored", { reason: "not_absolute" });
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
if (raw.length > MAX_PATH) {
|
|
78
|
+
opts.warn("shell_scratchpad_ignored", { reason: "too_long", length: raw.length, max: MAX_PATH });
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const canonical = resolve(raw);
|
|
82
|
+
const depth = canonical.split(sep).filter((s) => s.length > 0).length;
|
|
83
|
+
if (depth < 3) {
|
|
84
|
+
opts.warn("shell_scratchpad_ignored", { reason: "too_shallow", canonical });
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
await mkdir(canonical, { recursive: true });
|
|
89
|
+
return canonical;
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
opts.warn("shell_scratchpad_ignored", { reason: "mkdir_failed", error: err instanceof Error ? err.message : String(err) });
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
62
96
|
export async function purgeScratchpadDir(localDataRoot, sessionId) {
|
|
63
97
|
const dir = scratchpadDirFor(localDataRoot, sessionId);
|
|
64
98
|
try {
|
|
@@ -23,6 +23,20 @@ export interface FleetTaskRow {
|
|
|
23
23
|
};
|
|
24
24
|
toolUses?: number;
|
|
25
25
|
transcriptId?: string;
|
|
26
|
+
stoppedBy?: string;
|
|
27
|
+
usage?: {
|
|
28
|
+
totalTokens?: number;
|
|
29
|
+
toolUses?: number;
|
|
30
|
+
durationMs?: number;
|
|
31
|
+
tokens?: number;
|
|
32
|
+
turns?: number;
|
|
33
|
+
costMicroUsd?: number;
|
|
34
|
+
};
|
|
35
|
+
resumable?: boolean;
|
|
36
|
+
editedFiles?: Array<{
|
|
37
|
+
path: string;
|
|
38
|
+
edits: number;
|
|
39
|
+
}>;
|
|
26
40
|
}
|
|
27
41
|
export interface FleetWorkflowRow {
|
|
28
42
|
id: string;
|
|
@@ -161,9 +175,27 @@ export declare function fleetRunPublisher(bus: FleetEventBus | undefined, run: {
|
|
|
161
175
|
status?: string;
|
|
162
176
|
}) => void;
|
|
163
177
|
onChildTerminal: (taskId: string, status: string, altId?: string, toolUseId?: string) => boolean;
|
|
164
|
-
onTerminal: (status: string
|
|
178
|
+
onTerminal: (status: string, residuals?: {
|
|
179
|
+
usage?: FleetTaskRow["usage"];
|
|
180
|
+
stoppedBy?: string;
|
|
181
|
+
transcriptId?: string;
|
|
182
|
+
}) => void;
|
|
165
183
|
};
|
|
166
184
|
export type FleetRunPublisher = ReturnType<typeof fleetRunPublisher>;
|
|
185
|
+
export declare function fleetRunResiduals(result: {
|
|
186
|
+
sessionId?: string;
|
|
187
|
+
errorCode?: string;
|
|
188
|
+
stats?: {
|
|
189
|
+
turns?: number;
|
|
190
|
+
tokens?: number;
|
|
191
|
+
toolCalls?: number;
|
|
192
|
+
costMicroUsd?: number;
|
|
193
|
+
};
|
|
194
|
+
} | null | undefined): {
|
|
195
|
+
usage?: FleetTaskRow["usage"];
|
|
196
|
+
stoppedBy?: string;
|
|
197
|
+
transcriptId?: string;
|
|
198
|
+
};
|
|
167
199
|
export declare function fleetBackgroundChildPublisher(bus?: FleetEventBus, log?: (msg: string, fields: Record<string, unknown>) => void): (e: import("@sema-agent/core").BackgroundChildEvent) => void;
|
|
168
200
|
export declare function fleetRunLabels(objective: string | undefined): {
|
|
169
201
|
name: string;
|
package/dist/fleet/fleet-bus.js
CHANGED
|
@@ -136,21 +136,47 @@ export function fleetRunPublisher(bus, run) {
|
|
|
136
136
|
}
|
|
137
137
|
return hadRow;
|
|
138
138
|
},
|
|
139
|
-
onTerminal(status) {
|
|
139
|
+
onTerminal(status, residuals) {
|
|
140
140
|
settledLeg = true;
|
|
141
141
|
const fleetStatus = runStatusToFleet(status);
|
|
142
|
-
|
|
142
|
+
const terminal = fleetStatus === "completed" || fleetStatus === "failed" || fleetStatus === "killed";
|
|
143
|
+
const res = terminal && residuals
|
|
144
|
+
? {
|
|
145
|
+
...(residuals.usage ? { usage: residuals.usage } : {}),
|
|
146
|
+
...(residuals.stoppedBy ? { stoppedBy: residuals.stoppedBy } : {}),
|
|
147
|
+
...(residuals.transcriptId ? { transcriptId: residuals.transcriptId } : {}),
|
|
148
|
+
}
|
|
149
|
+
: {};
|
|
150
|
+
bus.publishTask({ id: run.runId, status: fleetStatus, tokens, elapsedMs: Date.now() - startedAt, ...res });
|
|
143
151
|
for (const cid of children)
|
|
144
152
|
bus.removeTask(cid);
|
|
145
153
|
children.clear();
|
|
146
154
|
childStartedAt.clear();
|
|
147
155
|
childByToolCall.clear();
|
|
148
|
-
if (
|
|
156
|
+
if (terminal) {
|
|
149
157
|
bus.removeTask(run.runId);
|
|
150
158
|
}
|
|
151
159
|
},
|
|
152
160
|
};
|
|
153
161
|
}
|
|
162
|
+
export function fleetRunResiduals(result) {
|
|
163
|
+
if (!result)
|
|
164
|
+
return {};
|
|
165
|
+
const s = result.stats;
|
|
166
|
+
const usage = s
|
|
167
|
+
? {
|
|
168
|
+
...(typeof s.tokens === "number" ? { totalTokens: s.tokens } : {}),
|
|
169
|
+
...(typeof s.turns === "number" ? { turns: s.turns } : {}),
|
|
170
|
+
...(typeof s.toolCalls === "number" ? { toolUses: s.toolCalls } : {}),
|
|
171
|
+
...(typeof s.costMicroUsd === "number" ? { costMicroUsd: s.costMicroUsd } : {}),
|
|
172
|
+
}
|
|
173
|
+
: {};
|
|
174
|
+
return {
|
|
175
|
+
...(Object.keys(usage).length > 0 ? { usage } : {}),
|
|
176
|
+
...(result.errorCode === "cancelled" ? { stoppedBy: "user" } : {}),
|
|
177
|
+
...(result.sessionId ? { transcriptId: result.sessionId } : {}),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
154
180
|
export function fleetBackgroundChildPublisher(bus, log) {
|
|
155
181
|
if (!bus)
|
|
156
182
|
return () => undefined;
|
|
@@ -325,7 +351,25 @@ export function fleetBackgroundChildPublisher(bus, log) {
|
|
|
325
351
|
return;
|
|
326
352
|
}
|
|
327
353
|
const status = e.status === "completed" ? "completed" : e.status === "killed" ? "killed" : "failed";
|
|
328
|
-
bus.publishTask({
|
|
354
|
+
bus.publishTask({
|
|
355
|
+
id: e.taskId, ...rowTags(m), status,
|
|
356
|
+
...(e.stoppedBy ? { stoppedBy: e.stoppedBy } : {}),
|
|
357
|
+
...(e.resumable !== undefined ? { resumable: e.resumable } : {}),
|
|
358
|
+
...(e.transcriptId ? { transcriptId: e.transcriptId } : {}),
|
|
359
|
+
...(e.editedFiles !== undefined ? { editedFiles: e.editedFiles.slice(0, 100).map((f) => ({ path: redactSecrets(f.path), edits: f.edits })) } : {}),
|
|
360
|
+
...(e.usage
|
|
361
|
+
? {
|
|
362
|
+
usage: {
|
|
363
|
+
...(e.usage.totalTokens !== undefined ? { totalTokens: e.usage.totalTokens } : {}),
|
|
364
|
+
...(e.usage.toolUses !== undefined ? { toolUses: e.usage.toolUses } : {}),
|
|
365
|
+
...(e.usage.durationMs !== undefined ? { durationMs: e.usage.durationMs } : {}),
|
|
366
|
+
...(e.usage.tokens !== undefined ? { tokens: e.usage.tokens } : {}),
|
|
367
|
+
...(e.usage.turns !== undefined ? { turns: e.usage.turns } : {}),
|
|
368
|
+
...(e.usage.costMicroUsd !== undefined ? { costMicroUsd: e.usage.costMicroUsd } : {}),
|
|
369
|
+
},
|
|
370
|
+
}
|
|
371
|
+
: {}),
|
|
372
|
+
});
|
|
329
373
|
bus.removeTask(e.taskId);
|
|
330
374
|
if (m.aliasUuid) {
|
|
331
375
|
const tail = ` ${m.aliasUuid}`;
|
package/dist/http/server.d.ts
CHANGED
package/dist/http/server.js
CHANGED
|
@@ -21,7 +21,7 @@ import { runInBackground, evictIfConflict, stripCheckpointToken, resumeAtHttpSta
|
|
|
21
21
|
import { looksLikeJwt } from "../auth-bridge.js";
|
|
22
22
|
import { redactSteerIn, STEER_IN_MAX_CHARS, STEER_IN_MAX_REQUEST_CHARS } from "../orchestration/workflow-agent-steer.js";
|
|
23
23
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationFoldKey, taskNotificationStreamKey, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
24
|
-
import { fleetRunPublisher, fleetRunLabels } from "../fleet/fleet-bus.js";
|
|
24
|
+
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals } from "../fleet/fleet-bus.js";
|
|
25
25
|
import { defaultSubagentTailBus, projectTailFrame } from "../fleet/subagent-tail-bus.js";
|
|
26
26
|
import { RateLimiter } from "../observability/rate-limit.js";
|
|
27
27
|
import { withPrincipal } from "../observability/principal-context.js";
|
|
@@ -679,6 +679,7 @@ export function createHttpServer(deps) {
|
|
|
679
679
|
toolOutput: true,
|
|
680
680
|
messageIdentity: true,
|
|
681
681
|
forwardSubagentEvents: true,
|
|
682
|
+
scratchpadDir: true,
|
|
682
683
|
subagentSteer: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
|
|
683
684
|
subagentResume: Boolean(deps.subagentSteerRegistry) && Boolean(deps.runStore),
|
|
684
685
|
taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: false, hooks: deps.config.requirePrincipal !== true },
|
|
@@ -853,8 +854,12 @@ export function createHttpServer(deps) {
|
|
|
853
854
|
return;
|
|
854
855
|
}
|
|
855
856
|
streamTaskId = earlyDurableTid;
|
|
857
|
+
let usageKey;
|
|
856
858
|
streamDetached = detachOnDisconnect;
|
|
857
859
|
sseHeaders(res, earlyDurableTid ? { "x-task-id": earlyDurableTid } : undefined);
|
|
860
|
+
if (earlyDurableTid) {
|
|
861
|
+
res.write(`event: meta\ndata: ${JSON.stringify({ type: "meta", taskId: earlyDurableTid, ...(prepared.spec.sessionId ? { sessionId: prepared.spec.sessionId } : {}) })}\n\n`);
|
|
862
|
+
}
|
|
858
863
|
const ac = new AbortController();
|
|
859
864
|
if (prepared.spec.sessionId)
|
|
860
865
|
markChildrenStoppedByUserOnAbort(ac.signal, prepared.spec.sessionId, prepared.auth?.principal);
|
|
@@ -921,6 +926,9 @@ export function createHttpServer(deps) {
|
|
|
921
926
|
const rs = deps.runStore;
|
|
922
927
|
const tid = durableTaskId;
|
|
923
928
|
ledgerSink = createLedgerSink({ appendEvent: (seq, type, data) => rs.appendEvent(tid, seq, type, data), persistThinking: deps.config.traceThinking });
|
|
929
|
+
usageKey = prepared.spec.sessionId;
|
|
930
|
+
if (usageKey)
|
|
931
|
+
deps.modelUsage?.register(usageKey);
|
|
924
932
|
}
|
|
925
933
|
if (deps.checkpointStore && prepared.spec.sessionId) {
|
|
926
934
|
await deps.checkpointStore.putCtx(prepared.spec.sessionId, { body: prepared.body, memoryScope: prepared.auth?.memoryScope });
|
|
@@ -961,11 +969,11 @@ export function createHttpServer(deps) {
|
|
|
961
969
|
throw e;
|
|
962
970
|
}
|
|
963
971
|
let fleetSettled = false;
|
|
964
|
-
const settleFleet = (status) => {
|
|
972
|
+
const settleFleet = (status, residuals) => {
|
|
965
973
|
if (fleetSettled)
|
|
966
974
|
return;
|
|
967
975
|
fleetSettled = true;
|
|
968
|
-
fleetPub?.onTerminal(status);
|
|
976
|
+
fleetPub?.onTerminal(status, residuals);
|
|
969
977
|
};
|
|
970
978
|
let liveStreamRef;
|
|
971
979
|
const subagentHandleEvictions = [];
|
|
@@ -1069,16 +1077,25 @@ export function createHttpServer(deps) {
|
|
|
1069
1077
|
}
|
|
1070
1078
|
finalizeTaskResult(ev.result, principal, prepared.spec.objective, prepared.spec.sessionId);
|
|
1071
1079
|
putRewindAnchor();
|
|
1072
|
-
settleFleet(finalResult?.status ?? "completed");
|
|
1080
|
+
settleFleet(finalResult?.status ?? "completed", fleetRunResiduals(finalResult));
|
|
1073
1081
|
if (ledgerSink && finalResult) {
|
|
1082
|
+
if (usageKey && deps.modelUsage && deps.runStore && durableTaskId) {
|
|
1083
|
+
const rs2 = deps.runStore;
|
|
1084
|
+
const tid2 = durableTaskId;
|
|
1085
|
+
const fr = finalResult;
|
|
1086
|
+
finalResult = await attachModelUsage(fr, { append: ledgerSink.append, getEvents: (_id, after) => rs2.getEvents(tid2, after), modelUsage: deps.modelUsage, taskId: usageKey }).catch(() => fr);
|
|
1087
|
+
}
|
|
1074
1088
|
await ledgerSink.onDone(finalResult);
|
|
1075
1089
|
ledgerTerminal = true;
|
|
1076
1090
|
}
|
|
1077
1091
|
res.write(`data: ${JSON.stringify({ ...ev, result: finalResult })}\n\n`);
|
|
1078
1092
|
continue;
|
|
1079
1093
|
}
|
|
1080
|
-
if (ledgerSink)
|
|
1094
|
+
if (ledgerSink) {
|
|
1081
1095
|
await ledgerSink.onEvent(ev);
|
|
1096
|
+
if (ev.type === "turn_end" && usageKey)
|
|
1097
|
+
await appendModelUsageDelta(ledgerSink.append, deps.modelUsage, usageKey);
|
|
1098
|
+
}
|
|
1082
1099
|
fleetPub?.onEvent(ev);
|
|
1083
1100
|
if (ev.type === "status")
|
|
1084
1101
|
deps.metrics?.inc("brain_retry_total", { phase: String(ev.phase) });
|
|
@@ -1164,7 +1181,16 @@ export function createHttpServer(deps) {
|
|
|
1164
1181
|
}
|
|
1165
1182
|
const withApproval = () => approvalCtx && deps.toolApproval ? deps.toolApproval.runWithContext(approvalCtx, withQuestion) : withQuestion();
|
|
1166
1183
|
await (deps.sendUserFile
|
|
1167
|
-
? deps.sendUserFile.runWithContext({
|
|
1184
|
+
? deps.sendUserFile.runWithContext({
|
|
1185
|
+
taskId: askTaskId,
|
|
1186
|
+
emit: async (f) => {
|
|
1187
|
+
void (await emitAsk(f));
|
|
1188
|
+
if (ledgerSink) {
|
|
1189
|
+
const { type, ...rest } = f;
|
|
1190
|
+
await ledgerSink.append(type, rest).catch(() => undefined);
|
|
1191
|
+
}
|
|
1192
|
+
},
|
|
1193
|
+
}, withApproval)
|
|
1168
1194
|
: withApproval());
|
|
1169
1195
|
if (durableTaskId && deps.runStore) {
|
|
1170
1196
|
if (finalResult?.status === "suspended" && deps.checkpointStore)
|
|
@@ -1232,7 +1258,7 @@ export function createHttpServer(deps) {
|
|
|
1232
1258
|
await ledgerSink.onDone(finalResult).catch(() => undefined);
|
|
1233
1259
|
}
|
|
1234
1260
|
await deps.runStore.setTerminal(durableTaskId, finalResult.status, finalResult, finalResult.errorMessage ?? null);
|
|
1235
|
-
settleFleet(finalResult.status);
|
|
1261
|
+
settleFleet(finalResult.status, fleetRunResiduals(finalResult));
|
|
1236
1262
|
}
|
|
1237
1263
|
else if (ac.signal.aborted) {
|
|
1238
1264
|
const err = "stream aborted before completion (run cancelled)";
|
|
@@ -1271,6 +1297,8 @@ export function createHttpServer(deps) {
|
|
|
1271
1297
|
finally {
|
|
1272
1298
|
uncountedBillableInflight--;
|
|
1273
1299
|
clearInterval(hb);
|
|
1300
|
+
if (usageKey)
|
|
1301
|
+
deps.modelUsage?.clear(usageKey);
|
|
1274
1302
|
}
|
|
1275
1303
|
if (!ranLive && resp && !res.writableEnded) {
|
|
1276
1304
|
res.write(`data: ${JSON.stringify({ type: "done", result: resp.body, replay: true })}\n\n`);
|
|
@@ -2163,7 +2191,17 @@ export function createHttpServer(deps) {
|
|
|
2163
2191
|
});
|
|
2164
2192
|
return;
|
|
2165
2193
|
}
|
|
2166
|
-
|
|
2194
|
+
const g14 = (() => {
|
|
2195
|
+
const d = out.details;
|
|
2196
|
+
if (!d || d.error !== undefined || d.retrieval_status === "not_ready")
|
|
2197
|
+
return {};
|
|
2198
|
+
if (d.type === "background_bash")
|
|
2199
|
+
return { cursorSemantics: d.details && "bytesDroppedBeforeCursor" in d.details ? "cursor" : "full" };
|
|
2200
|
+
if (d.type === "monitor" || d.type === "background_agent")
|
|
2201
|
+
return { cursorSemantics: "full" };
|
|
2202
|
+
return {};
|
|
2203
|
+
})();
|
|
2204
|
+
sendJson(res, 200, { taskId: runId, target, content: out.content, output: out.details, ...g14 });
|
|
2167
2205
|
return;
|
|
2168
2206
|
}
|
|
2169
2207
|
const subVerbMatch = req.method === "POST" ? (RUN_SUBAGENT_STEER_RE.exec(url) ?? RUN_SUBAGENT_RESUME_RE.exec(url)) : null;
|
|
@@ -5078,11 +5116,11 @@ export function createHttpServer(deps) {
|
|
|
5078
5116
|
? fleetRunPublisher(deps.fleetBus, { runId: taskId, scope: fleetScope, rootTaskId: sessionId, ...fleetRunLabels(resumeObjective) })
|
|
5079
5117
|
: undefined;
|
|
5080
5118
|
let fleetSettled = false;
|
|
5081
|
-
const settleFleet = (status) => {
|
|
5119
|
+
const settleFleet = (status, residuals) => {
|
|
5082
5120
|
if (fleetSettled)
|
|
5083
5121
|
return;
|
|
5084
5122
|
fleetSettled = true;
|
|
5085
|
-
fleetPub?.onTerminal(status);
|
|
5123
|
+
fleetPub?.onTerminal(status, residuals);
|
|
5086
5124
|
};
|
|
5087
5125
|
let result;
|
|
5088
5126
|
let reopenConfirmedPark = false;
|
|
@@ -5445,7 +5483,7 @@ export function createHttpServer(deps) {
|
|
|
5445
5483
|
else
|
|
5446
5484
|
await deps.runStore.setTerminal(taskId, result.status, stripCheckpointToken(result), result.errorMessage ?? null);
|
|
5447
5485
|
}
|
|
5448
|
-
settleFleet(checkpointReopened ? "suspended" : result.status);
|
|
5486
|
+
settleFleet(checkpointReopened ? "suspended" : result.status, fleetRunResiduals(result));
|
|
5449
5487
|
return {
|
|
5450
5488
|
status: 200,
|
|
5451
5489
|
body: {
|
package/dist/main.js
CHANGED
|
@@ -21,7 +21,7 @@ import { createSendUserFileIssuer } from "./plugins/send-user-file.js";
|
|
|
21
21
|
import { withLedgerRecording } from "./plugins/send-file-ledger.js";
|
|
22
22
|
import { basename, resolve } from "node:path";
|
|
23
23
|
import { stat as fsStat, readFile as fsReadFile } from "node:fs/promises";
|
|
24
|
-
import { buildEnvFacts, egressForRemoteExec, ensureScratchpadDir, purgeScratchpadDir, sweepStaleScratchpads, resumeFactsForLane } from "./env-facts.js";
|
|
24
|
+
import { acceptShellScratchpadDir, buildEnvFacts, egressForRemoteExec, ensureScratchpadDir, purgeScratchpadDir, sweepStaleScratchpads, resumeFactsForLane } from "./env-facts.js";
|
|
25
25
|
import { normalizeSuggestNextPrompts, normalizeResilience, normalizeAttachments, normalizeResumeAtMode, resolveTaskLimits, taskAgentsSpecFragment, retainBackgroundProcessesFromBody, toolNameListFromBody, promptProfileFromBody } from "./spec-fields.js";
|
|
26
26
|
import { createBrain, brainSummary } from "./brain.js";
|
|
27
27
|
import { loadConfig, logConfigDiagnostics } from "./config.js";
|
|
@@ -434,7 +434,7 @@ async function main() {
|
|
|
434
434
|
await backend.ensureSchema();
|
|
435
435
|
}
|
|
436
436
|
catch (err) {
|
|
437
|
-
if (config.sessionBackend === "auto") {
|
|
437
|
+
if (config.sessionBackend === "auto" || (config.dbBackend === "local" && !config.dbBackendExplicit)) {
|
|
438
438
|
logger.warn("store_db_unreachable_fallback_memory", { backend: backend.kind, error: err instanceof Error ? err.message : String(err) });
|
|
439
439
|
storeBackendDegraded = true;
|
|
440
440
|
await backend.close().catch(() => undefined);
|
|
@@ -499,6 +499,12 @@ async function main() {
|
|
|
499
499
|
}
|
|
500
500
|
else {
|
|
501
501
|
memoryEngine = memoryEngineBackendFor(config);
|
|
502
|
+
if (memoryEngine && process.env.LOCAL_DATA_ROOT && !config.memoryEngineDir) {
|
|
503
|
+
logger.warn("memory_engine_dir_defaulted", {
|
|
504
|
+
root: memoryEngine.root,
|
|
505
|
+
hint: "LOCAL_DATA_ROOT is set but MEMORY_ENGINE_DIR is not — the memory engine is sharing the machine-wide default; set MEMORY_ENGINE_DIR to isolate it under your data root",
|
|
506
|
+
});
|
|
507
|
+
}
|
|
502
508
|
}
|
|
503
509
|
let rosterStore;
|
|
504
510
|
{
|
|
@@ -2181,9 +2187,13 @@ async function main() {
|
|
|
2181
2187
|
effectiveSettings = Object.keys(rest2).length > 0 ? rest2 : undefined;
|
|
2182
2188
|
}
|
|
2183
2189
|
const hostSemanticsLane = config.remoteExec === undefined || config.remoteExec.provider === "host";
|
|
2184
|
-
const
|
|
2185
|
-
|
|
2186
|
-
|
|
2190
|
+
const shellScratchpad = await acceptShellScratchpadDir(body.scratchpadDir, {
|
|
2191
|
+
requirePrincipal: config.requirePrincipal,
|
|
2192
|
+
hostSemanticsLane,
|
|
2193
|
+
warn: (msg, meta) => logger.warn(msg, { ...(meta ?? {}), sessionId: auth?.sessionId ?? null }),
|
|
2194
|
+
});
|
|
2195
|
+
const scratchpadDir = shellScratchpad ??
|
|
2196
|
+
(hostSemanticsLane && auth?.sessionId ? await ensureScratchpadDir(config.localDataRoot ?? localRoot, auth.sessionId) : undefined);
|
|
2187
2197
|
if (effectiveSettings) {
|
|
2188
2198
|
const fsWriteGate = hostSemanticsLane
|
|
2189
2199
|
? (() => {
|
package/dist/runs.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { Metrics } from "./observability/metrics.js";
|
|
|
5
5
|
import type { ModelUsageTracker, PromptManifestTracker } from "./budget.js";
|
|
6
6
|
import type { ElicitationCoordinator } from "./elicitation.js";
|
|
7
7
|
import type { QuestionCoordinator } from "./question.js";
|
|
8
|
-
import type
|
|
8
|
+
import { type FleetRunPublisher } from "./fleet/fleet-bus.js";
|
|
9
9
|
import { type WorkflowCompletionInbox } from "./orchestration/workflow-completion-inbox.js";
|
|
10
10
|
export declare function backgroundAgentOutput(registry: typeof defaultTaskRegistry, handle: string, access: {
|
|
11
11
|
owner: string;
|
package/dist/runs.js
CHANGED
|
@@ -3,6 +3,7 @@ import { withPrincipal } from "./observability/principal-context.js";
|
|
|
3
3
|
import { redactSecrets } from "./trace/redact.js";
|
|
4
4
|
import { taskNotificationEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "./trace/project.js";
|
|
5
5
|
import { createLedgerSink } from "./trace/ledger-sink.js";
|
|
6
|
+
import { fleetRunResiduals } from "./fleet/fleet-bus.js";
|
|
6
7
|
import { defaultSubagentTailBus, projectTailFrame } from "./fleet/subagent-tail-bus.js";
|
|
7
8
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKey, NotifiedKeys } from "./orchestration/workflow-completion-inbox.js";
|
|
8
9
|
export async function backgroundAgentOutput(registry, handle, access, agentStore) {
|
|
@@ -365,13 +366,16 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
|
|
|
365
366
|
legLive = false;
|
|
366
367
|
if (fleetPublisher) {
|
|
367
368
|
if (reached?.kind === "done")
|
|
368
|
-
fleetPublisher.onTerminal(reached.result.errorCode === "cancelled" ? "cancelled" : reached.result.status);
|
|
369
|
+
fleetPublisher.onTerminal(reached.result.errorCode === "cancelled" ? "cancelled" : reached.result.status, fleetRunResiduals(reached.result));
|
|
369
370
|
else if (reached?.kind === "suspended")
|
|
370
371
|
fleetPublisher.onTerminal("suspended");
|
|
371
372
|
else if (reached?.kind === "needs_review")
|
|
372
373
|
fleetPublisher.onTerminal("needs_review");
|
|
373
374
|
else
|
|
374
|
-
fleetPublisher.onTerminal(cancelCtrl.signal.aborted ? "cancelled" : "failed"
|
|
375
|
+
fleetPublisher.onTerminal(cancelCtrl.signal.aborted ? "cancelled" : "failed", {
|
|
376
|
+
...(cancelCtrl.signal.aborted ? { stoppedBy: "user" } : {}),
|
|
377
|
+
...(spec.sessionId ? { transcriptId: spec.sessionId } : {}),
|
|
378
|
+
});
|
|
375
379
|
}
|
|
376
380
|
if (inflight?.get(taskId) === cancelCtrl)
|
|
377
381
|
inflight.delete(taskId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.292.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",
|