@sema-agent/server 1.250.0 → 1.252.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.
@@ -21,6 +21,7 @@ import { looksLikeJwt } from "../auth-bridge.js";
21
21
  import { redactSteerIn } from "../orchestration/workflow-agent-steer.js";
22
22
  import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationFoldKey, taskNotificationStreamKey, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
23
23
  import { fleetRunPublisher, fleetRunLabels } from "../fleet/fleet-bus.js";
24
+ import { defaultSubagentTailBus, projectTailFrame } from "../fleet/subagent-tail-bus.js";
24
25
  import { RateLimiter } from "../observability/rate-limit.js";
25
26
  import { withPrincipal } from "../observability/principal-context.js";
26
27
  import { projectEvents, runSummary, mapTraceEvent, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
@@ -168,6 +169,7 @@ const WORKFLOW_AGENT_STEER_RE = /^\/v1\/workflows\/([^/]+)\/agents\/([^/]+)\/ste
168
169
  const RUN_SUBAGENT_STEER_RE = /^\/v1\/runs\/([^/]+)\/subagents\/([^/]+)\/steer$/;
169
170
  const RUN_SUBAGENT_RESUME_RE = /^\/v1\/runs\/([^/]+)\/subagents\/([^/]+)\/resume$/;
170
171
  const RUN_SUBAGENT_OUTPUT_RE = /^\/v1\/runs\/([^/]+)\/subagents\/([^/]+)\/output$/;
172
+ const RUN_SUBAGENT_STREAM_RE = /^\/v1\/runs\/([^/]+)\/subagents\/([^/]+)\/stream$/;
171
173
  const RUN_TASK_OUTPUT_RE = /^\/v1\/runs\/([^/]+)\/tasks\/([^/]+)\/output$/;
172
174
  const RUN_TASK_STOP_RE = /^\/v1\/runs\/([^/]+)\/tasks\/([^/]+)\/stop$/;
173
175
  const HAND_TOOL_NAMES = Object.keys(HAND_TOOL_EFFECTS);
@@ -677,6 +679,7 @@ export function createHttpServer(deps) {
677
679
  appendSystemPrompt: true,
678
680
  compactionModel: true,
679
681
  subagentOutput: Boolean(deps.subagentTaskOutput) && Boolean(deps.runStore),
682
+ subagentStream: Boolean(deps.subagentTaskOutput) && Boolean(deps.runStore),
680
683
  taskHandles: Boolean(deps.taskHandleOutput) && Boolean(deps.taskHandleStop) && Boolean(deps.runStore),
681
684
  taskAgents: deps.config.requirePrincipal !== true,
682
685
  retainBackgroundProcesses: deps.config.requirePrincipal !== true,
@@ -982,6 +985,14 @@ export function createHttpServer(deps) {
982
985
  onForwardEvent: (e) => {
983
986
  fleetPub?.onForwardEvent(e);
984
987
  ledgerSink?.onForwardEvent(e);
988
+ {
989
+ const bg = e.bgAgentId;
990
+ if (bg !== undefined && defaultSubagentTailBus.hasSubscribers(bg)) {
991
+ const f = projectTailFrame(e);
992
+ if (f)
993
+ defaultSubagentTailBus.publish(bg, f);
994
+ }
995
+ }
985
996
  if (res.writableEnded)
986
997
  return;
987
998
  const t = e.type;
@@ -1008,6 +1019,9 @@ export function createHttpServer(deps) {
1008
1019
  onTaskNotification: (n) => {
1009
1020
  if (n.task_type === "workflow")
1010
1021
  return;
1022
+ if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
1023
+ defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
1024
+ }
1011
1025
  const hadRow = fleetPub?.onChildTerminal(n.sessionId ?? n.task_id, n.status, n.task_id, n.toolUseId) ?? false;
1012
1026
  const deliverable = syncLegLive && !res.writableEnded && !res.destroyed;
1013
1027
  const parked = !deliverable && Boolean(deps.workflowCompletionInbox && prepared.spec.sessionId);
@@ -1975,6 +1989,76 @@ export function createHttpServer(deps) {
1975
1989
  sendJson(res, 200, { taskId: runId, target, content: out.content, output: out.details });
1976
1990
  return;
1977
1991
  }
1992
+ const subStreamMatch = req.method === "GET" ? RUN_SUBAGENT_STREAM_RE.exec(url) : null;
1993
+ if (subStreamMatch) {
1994
+ if (!deps.runStore || !deps.subagentTaskOutput) {
1995
+ sendJson(res, 501, { error: "subagent streams require the run store" });
1996
+ return;
1997
+ }
1998
+ const principal = gatedPrincipal(req, deps.config);
1999
+ if (deps.config.requirePrincipal && principal === undefined) {
2000
+ sendJson(res, 401, { error: `missing principal header '${deps.config.principalHeader}'` });
2001
+ return;
2002
+ }
2003
+ const runId = safeDecode(subStreamMatch[1]);
2004
+ const target = safeDecode(subStreamMatch[2]);
2005
+ if (runId === null || target === null) {
2006
+ sendJson(res, 400, { error: "malformed subagent path (invalid percent-encoding)" });
2007
+ return;
2008
+ }
2009
+ const trusted = explicitOperatorOk(principal, deps.config.operatorPrincipals);
2010
+ const run = await deps.runStore.getRun(runId);
2011
+ if (!run || (!trusted && run.owner !== null && run.owner !== principal)) {
2012
+ sendJson(res, 404, { error: "run not found" });
2013
+ return;
2014
+ }
2015
+ const callerSession = new URL(req.url ?? "", "http://x").searchParams.get("session");
2016
+ if (!trusted && run.sessionId && callerSession !== run.sessionId) {
2017
+ sendJson(res, 404, { error: "run not found" });
2018
+ return;
2019
+ }
2020
+ const probe = await deps.subagentTaskOutput(target, { owner: runId, scope: run.owner ?? "default", ...(run.sessionId ? { sessionId: run.sessionId } : {}) });
2021
+ const probeDetails = probe.details;
2022
+ if (probeDetails?.error === "not_found" || probeDetails?.type !== "background_agent") {
2023
+ sendJson(res, 404, { error: `no background agent "${target}" under this run (unknown handle, not this run's child, or already reaped — bg children live in the replica-local registry for the parent's lifetime; wa… workflow-agent rows are read via the workflow journal, not this verb)` });
2024
+ return;
2025
+ }
2026
+ sseHeaders(res);
2027
+ res.write(`event: meta\ndata: ${JSON.stringify({ version: 1, runId, target, status: probeDetails.status ?? "running", live: "replica-local", replayFace: "GET /v1/runs/:id/subagents/:handle/output" })}\n\n`);
2028
+ if (probeDetails.status !== "running" && probeDetails.status !== "pending") {
2029
+ res.end();
2030
+ return;
2031
+ }
2032
+ const it = defaultSubagentTailBus.subscribe(target);
2033
+ let closed = false;
2034
+ const hb = setInterval(() => {
2035
+ if (!res.writableEnded)
2036
+ res.write(`event: heartbeat\ndata: {}\n\n`);
2037
+ }, 15_000);
2038
+ if (typeof hb.unref === "function")
2039
+ hb.unref();
2040
+ req.on("close", () => {
2041
+ closed = true;
2042
+ void it.return?.();
2043
+ });
2044
+ try {
2045
+ for (;;) {
2046
+ const n = await it.next();
2047
+ if (n.done || closed || res.writableEnded)
2048
+ break;
2049
+ res.write(`event: forward\ndata: ${JSON.stringify(n.value)}\n\n`);
2050
+ if (n.value.type === "task_settled")
2051
+ break;
2052
+ }
2053
+ }
2054
+ finally {
2055
+ clearInterval(hb);
2056
+ void it.return?.();
2057
+ if (!res.writableEnded)
2058
+ res.end();
2059
+ }
2060
+ return;
2061
+ }
1978
2062
  const taskVerbMatch = req.method === "GET" ? RUN_TASK_OUTPUT_RE.exec(url) : req.method === "POST" ? RUN_TASK_STOP_RE.exec(url) : null;
1979
2063
  if (taskVerbMatch) {
1980
2064
  const stopVerb = req.method === "POST";
@@ -4875,6 +4959,14 @@ export function createHttpServer(deps) {
4875
4959
  const stream = await deps.runner.resumeStream(token, outcome, { ...taskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, {
4876
4960
  onForwardEvent: (e) => {
4877
4961
  fleetPub?.onForwardEvent(e);
4962
+ {
4963
+ const bg = e.bgAgentId;
4964
+ if (bg !== undefined && defaultSubagentTailBus.hasSubscribers(bg)) {
4965
+ const f = projectTailFrame(e);
4966
+ if (f)
4967
+ defaultSubagentTailBus.publish(bg, f);
4968
+ }
4969
+ }
4878
4970
  if (!rs || !taskId)
4879
4971
  return;
4880
4972
  const t = e.type;
@@ -4891,6 +4983,9 @@ export function createHttpServer(deps) {
4891
4983
  onTaskNotification: (n) => {
4892
4984
  if (n.task_type === "workflow")
4893
4985
  return;
4986
+ if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
4987
+ defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
4988
+ }
4894
4989
  const hadRow = fleetPub?.onChildTerminal(n.sessionId ?? n.task_id, n.status, n.task_id, n.toolUseId) ?? false;
4895
4990
  const parked = !resumeLegLive && Boolean(deps.workflowCompletionInbox && sessionId);
4896
4991
  deps.logger?.info?.("task_notification_observed", { route: "resume", taskId: n.task_id, taskType: n.task_type, status: n.status, hadFleetRow: hadRow, legLive: resumeLegLive, parkedDurable: parked });