@sema-agent/server 1.187.0 → 1.189.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.
Files changed (38) hide show
  1. package/dist/capabilities/sandbox-file-send.d.ts +20 -0
  2. package/dist/capabilities/sandbox-file-send.d.ts.map +1 -0
  3. package/dist/capabilities/sandbox-file-send.js +106 -0
  4. package/dist/capabilities/sandbox-file-send.js.map +1 -0
  5. package/dist/capabilities/send-user-file-tool.d.ts +4 -5
  6. package/dist/capabilities/send-user-file-tool.d.ts.map +1 -1
  7. package/dist/capabilities/send-user-file-tool.js +6 -7
  8. package/dist/capabilities/send-user-file-tool.js.map +1 -1
  9. package/dist/config.d.ts +2 -0
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +2 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/http/server.d.ts +1 -0
  14. package/dist/http/server.d.ts.map +1 -1
  15. package/dist/http/server.js +41 -16
  16. package/dist/http/server.js.map +1 -1
  17. package/dist/main.js +40 -14
  18. package/dist/main.js.map +1 -1
  19. package/dist/orchestration/workflow-completion-inbox.d.ts +12 -0
  20. package/dist/orchestration/workflow-completion-inbox.d.ts.map +1 -1
  21. package/dist/orchestration/workflow-completion-inbox.js +18 -0
  22. package/dist/orchestration/workflow-completion-inbox.js.map +1 -1
  23. package/dist/plugins/s3-presign.d.ts +12 -1
  24. package/dist/plugins/s3-presign.d.ts.map +1 -1
  25. package/dist/plugins/s3-presign.js +14 -5
  26. package/dist/plugins/s3-presign.js.map +1 -1
  27. package/dist/plugins/send-user-file.d.ts +8 -0
  28. package/dist/plugins/send-user-file.d.ts.map +1 -1
  29. package/dist/plugins/send-user-file.js +72 -1
  30. package/dist/plugins/send-user-file.js.map +1 -1
  31. package/dist/runs.d.ts.map +1 -1
  32. package/dist/runs.js +16 -9
  33. package/dist/runs.js.map +1 -1
  34. package/dist/sighup-idle.d.ts +12 -0
  35. package/dist/sighup-idle.d.ts.map +1 -0
  36. package/dist/sighup-idle.js +53 -0
  37. package/dist/sighup-idle.js.map +1 -0
  38. package/package.json +1 -1
@@ -17,7 +17,7 @@ import { manifestToIndexEntry } from "../images/manifest.js";
17
17
  import { runInBackground, evictIfConflict, stripCheckpointToken, resumeAtHttpStatus, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
18
18
  import { looksLikeJwt } from "../auth-bridge.js";
19
19
  import { redactSteerIn } from "../orchestration/workflow-agent-steer.js";
20
- import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationFoldKey } from "../orchestration/workflow-completion-inbox.js";
20
+ import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationFoldKey, taskNotificationStreamKey, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
21
21
  import { fleetRunPublisher, fleetRunLabels } from "../fleet/fleet-bus.js";
22
22
  import { RateLimiter } from "../observability/rate-limit.js";
23
23
  import { withPrincipal } from "../observability/principal-context.js";
@@ -181,6 +181,14 @@ export function createHttpServer(deps) {
181
181
  let uncountedBillableInflight = 0;
182
182
  if (deps.drainState)
183
183
  deps.drainState.inflight = () => new Set([...inflightRuns.keys(), ...steerableRuns.keys()]).size + uncountedBillableInflight + (deps.leaderEndpoint?.inflight?.() ?? 0);
184
+ let lastBillableActivityAt = Date.now();
185
+ const noteActivity = (url) => {
186
+ if (url === "/health" || url.startsWith("/metrics"))
187
+ return;
188
+ lastBillableActivityAt = Date.now();
189
+ };
190
+ if (deps.drainState)
191
+ deps.drainState.lastActivityAt = () => lastBillableActivityAt;
184
192
  if (deps.hookWakeBus) {
185
193
  deps.hookWakeBus.deliver = async (sessionId, text) => {
186
194
  try {
@@ -249,6 +257,7 @@ export function createHttpServer(deps) {
249
257
  const startedAt = Date.now();
250
258
  const method = req.method ?? "GET";
251
259
  const url = (req.url ?? "/").split("?")[0];
260
+ noteActivity(url);
252
261
  let logged = false;
253
262
  const record = (viaClose) => {
254
263
  if (logged)
@@ -674,6 +683,7 @@ export function createHttpServer(deps) {
674
683
  }, 15_000);
675
684
  let ranLive = false;
676
685
  let resp;
686
+ uncountedBillableInflight++;
677
687
  try {
678
688
  resp = await idemCache.run(idemKey, async () => {
679
689
  ranLive = true;
@@ -725,7 +735,7 @@ export function createHttpServer(deps) {
725
735
  let liveStreamRef;
726
736
  const subagentHandleEvictions = [];
727
737
  let syncLegLive = true;
728
- const syncNotifiedKeys = new Set();
738
+ const syncNotifiedKeys = new NotifiedKeys();
729
739
  fleetPub?.onStart();
730
740
  try {
731
741
  const streamBody = async () => {
@@ -767,9 +777,9 @@ export function createHttpServer(deps) {
767
777
  void deps.workflowCompletionInbox.enqueue(taskNotificationInboxEntry(prepared.spec.sessionId, askOwner, n, Date.now(), durableTaskId)).catch((err) => deps.logger?.warn?.("park_enqueue_failed", { route: "sync-stream", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
768
778
  }
769
779
  else if (deliverable) {
770
- const key = `${n.task_id}:${n.status}`;
771
- if (!syncNotifiedKeys.has(key)) {
772
- syncNotifiedKeys.add(key);
780
+ const key = taskNotificationStreamKey(n);
781
+ if (syncNotifiedKeys.get(key) === undefined) {
782
+ syncNotifiedKeys.set(key, Promise.resolve(true));
773
783
  res.write(`data: ${JSON.stringify({ type: "task_notification", ...taskNotificationEventData({ notification: n }) })}\n\n`);
774
784
  }
775
785
  }
@@ -814,9 +824,13 @@ export function createHttpServer(deps) {
814
824
  }
815
825
  else if (ev.type === "task_notification") {
816
826
  const tnv = ev.notification;
817
- if (tnv?.task_id)
818
- syncNotifiedKeys.add(`${tnv.task_id}:${tnv.status}`);
819
- res.write(`data: ${JSON.stringify({ type: "task_notification", ...taskNotificationEventData(ev) })}\n\n`);
827
+ const tnvKey = tnv?.task_id ? taskNotificationStreamKey({ task_type: tnv.task_type, task_id: tnv.task_id, status: String(tnv.status), seq: tnv.seq }) : undefined;
828
+ const tnvPrior = tnvKey !== undefined ? syncNotifiedKeys.get(tnvKey) : undefined;
829
+ if (tnvPrior === undefined || !(await tnvPrior)) {
830
+ if (tnvKey !== undefined)
831
+ syncNotifiedKeys.set(tnvKey, Promise.resolve(true));
832
+ res.write(`data: ${JSON.stringify({ type: "task_notification", ...taskNotificationEventData(ev) })}\n\n`);
833
+ }
820
834
  }
821
835
  else if (ev.type === "reasoning_delta" && typeof ev.delta === "string") {
822
836
  res.write(`data: ${JSON.stringify({ ...ev, delta: redactSecrets(ev.delta) })}\n\n`);
@@ -845,6 +859,9 @@ export function createHttpServer(deps) {
845
859
  await emitPendingWorkflowCompletions(deps.workflowCompletionInbox, prepared.spec.sessionId, askOwner, (frame) => {
846
860
  if (closed || res.writableEnded || res.destroyed)
847
861
  throw new Error("stream ended before the completion frame was written");
862
+ const f = frame;
863
+ if (f.type === "task_notification" && f.task_id)
864
+ syncNotifiedKeys.set(taskNotificationStreamKey({ task_type: f.task_type, task_id: f.task_id, status: String(f.status), seq: f.seq }), Promise.resolve(true));
848
865
  res.write(`data: ${JSON.stringify(frame)}\n\n`);
849
866
  }, { route: "sync-stream-open", connection: "live-sse", log: (m, x) => deps.logger?.info?.(m, x) });
850
867
  const withElicit = deps.elicitation
@@ -896,6 +913,7 @@ export function createHttpServer(deps) {
896
913
  throw e;
897
914
  }
898
915
  finally {
916
+ uncountedBillableInflight--;
899
917
  clearInterval(hb);
900
918
  }
901
919
  if (!ranLive && resp && !res.writableEnded) {
@@ -3780,11 +3798,15 @@ export function createHttpServer(deps) {
3780
3798
  }
3781
3799
  try {
3782
3800
  result = await withPrincipal(principal, async () => {
3801
+ const resumeNotifiedKeys = new NotifiedKeys();
3783
3802
  if (deps.runStore && taskId) {
3784
3803
  const rs0 = deps.runStore;
3785
3804
  let drainSeq = (await rs0.maxSeq(taskId)) + 1;
3786
3805
  await emitPendingWorkflowCompletions(deps.workflowCompletionInbox, sessionId, principal ?? null, (frame) => {
3787
3806
  const { type, ...rest } = frame;
3807
+ const f = frame;
3808
+ if (f.type === "task_notification" && f.task_id)
3809
+ resumeNotifiedKeys.set(taskNotificationStreamKey({ task_type: f.task_type, task_id: f.task_id, status: String(f.status), seq: f.seq }), Promise.resolve(true));
3788
3810
  return rs0.appendEvent(taskId, drainSeq++, type, rest);
3789
3811
  }, { route: "resume-open", connection: "durable-append", log: (m, x) => deps.logger?.info?.(m, x) });
3790
3812
  }
@@ -3817,7 +3839,6 @@ export function createHttpServer(deps) {
3817
3839
  let reasoning = "";
3818
3840
  const persistThinking = deps.config.traceThinking;
3819
3841
  const append = (type, data) => (rs && taskId ? rs.appendEvent(taskId, ++seq, type, data) : Promise.resolve());
3820
- const resumeNotifiedKeys = new Set();
3821
3842
  const anchorOwner = principal ?? null;
3822
3843
  const getLeafId = deps.sessionStorage?.getLeafId?.bind(deps.sessionStorage);
3823
3844
  const captureTurnAnchor = deps.resumeAnchorStore && getLeafId
@@ -3866,10 +3887,9 @@ export function createHttpServer(deps) {
3866
3887
  void deps.workflowCompletionInbox.enqueue(taskNotificationInboxEntry(sessionId, principal ?? null, n, Date.now(), taskId)).catch((err) => deps.logger?.warn?.("park_enqueue_failed", { route: "resume", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
3867
3888
  }
3868
3889
  else if (resumeLegLive) {
3869
- const key = `${n.task_id}:${n.status}`;
3870
- if (!resumeNotifiedKeys.has(key)) {
3871
- resumeNotifiedKeys.add(key);
3872
- void append("task_notification", taskNotificationEventData({ notification: n })).catch(() => undefined);
3890
+ const key = taskNotificationStreamKey(n);
3891
+ if (resumeNotifiedKeys.get(key) === undefined) {
3892
+ resumeNotifiedKeys.set(key, append("task_notification", taskNotificationEventData({ notification: n })).then(() => true, () => false));
3873
3893
  }
3874
3894
  }
3875
3895
  },
@@ -3930,9 +3950,14 @@ export function createHttpServer(deps) {
3930
3950
  case "task_notification": {
3931
3951
  await flush();
3932
3952
  const tnr = ev.notification;
3933
- if (tnr?.task_id)
3934
- resumeNotifiedKeys.add(`${tnr.task_id}:${tnr.status}`);
3935
- await append("task_notification", taskNotificationEventData(ev));
3953
+ const tnrKey = tnr?.task_id ? taskNotificationStreamKey({ task_type: tnr.task_type, task_id: tnr.task_id, status: String(tnr.status), seq: tnr.seq }) : undefined;
3954
+ const tnrPrior = tnrKey !== undefined ? resumeNotifiedKeys.get(tnrKey) : undefined;
3955
+ if (tnrPrior === undefined || !(await tnrPrior)) {
3956
+ const write = append("task_notification", taskNotificationEventData(ev));
3957
+ if (tnrKey !== undefined)
3958
+ resumeNotifiedKeys.set(tnrKey, write.then(() => true, () => false));
3959
+ await write;
3960
+ }
3936
3961
  break;
3937
3962
  }
3938
3963
  case "turn_end": {