@agent-native/core 0.78.4 → 0.78.7

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.
@@ -13,7 +13,7 @@ import { completeRun as completeProgressRun, startRun as startProgressRun, updat
13
13
  import { getFrontmatterValue, parseFrontmatter, } from "../resources/metadata.js";
14
14
  import { isDeployCredentialFallbackAllowed, readDeployCredentialEnv, } from "../server/credential-provider.js";
15
15
  import { readBody } from "../server/h3-helpers.js";
16
- import { getRequestRunContext, ensureRequestRunContext, getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
16
+ import { getRequestRunContext, ensureRequestRunContext, getRequestContext, getRequestOrgId, getRequestUserEmail, runWithRequestContext, } from "../server/request-context.js";
17
17
  import { fireInternalDispatch } from "../server/self-dispatch.js";
18
18
  import { isReasoningEffort, normalizeReasoningEffortForModel, } from "../shared/reasoning-effort.js";
19
19
  import { applyContextDirectives } from "./context-xray/apply-directives.js";
@@ -2537,13 +2537,12 @@ export async function runAgentLoop(opts) {
2537
2537
  let mcpApp;
2538
2538
  try {
2539
2539
  const timeoutSignal = AbortSignal.timeout(toolTimeoutMs);
2540
- // Keep a reference to the action promise so we can attach a zombie-
2541
- // detection continuation AFTER Promise.race abandons it on run abort.
2542
- // The promise itself is not awaited here — Promise.race owns the await.
2543
- const actionPromise = Promise.resolve(actionEntry.run(toolCall.input, {
2540
+ const actionUserEmail = opts.ownerEmail ?? getRequestUserEmail();
2541
+ const actionOrgId = opts.orgId ?? getRequestOrgId() ?? null;
2542
+ const actionContext = {
2544
2543
  send,
2545
- userEmail: getRequestUserEmail(),
2546
- orgId: getRequestOrgId() ?? null,
2544
+ userEmail: actionUserEmail ?? undefined,
2545
+ orgId: actionOrgId,
2547
2546
  caller: "tool",
2548
2547
  attachments: opts.attachments,
2549
2548
  signal,
@@ -2552,7 +2551,18 @@ export async function runAgentLoop(opts) {
2552
2551
  actionName: toolCall.name,
2553
2552
  ...(opts.threadId ? { threadId: opts.threadId } : {}),
2554
2553
  ...(opts.turnId ? { turnId: opts.turnId } : {}),
2555
- }));
2554
+ };
2555
+ const requestContext = getRequestContext();
2556
+ const invokeAction = () => actionEntry.run(toolCall.input, actionContext);
2557
+ // Keep a reference to the action promise so we can attach a zombie-
2558
+ // detection continuation AFTER Promise.race abandons it on run abort.
2559
+ // The promise itself is not awaited here — Promise.race owns the await.
2560
+ const actionPromise = Promise.resolve(runWithRequestContext({
2561
+ ...(requestContext ?? {}),
2562
+ ...(actionUserEmail ? { userEmail: actionUserEmail } : {}),
2563
+ ...(actionOrgId ? { orgId: actionOrgId } : {}),
2564
+ ...(requestContext?.run ? { run: requestContext.run } : {}),
2565
+ }, invokeAction));
2556
2566
  // When the run is aborted (soft-timeout / user cancel) while this tool
2557
2567
  // call is in flight, Promise.race below will throw "Run aborted" and the
2558
2568
  // action's promise becomes a zombie — it keeps running but its result is
@@ -3170,6 +3180,30 @@ export function createProductionAgentHandler(options) {
3170
3180
  ? requestEffort
3171
3181
  : options.reasoningEffort);
3172
3182
  options.onEngineResolved?.(engine, model);
3183
+ // DIAGNOSTIC-ONLY: localize where the worker stalls AFTER model_done. These
3184
+ // land in `worker_stage` (foreground-independent), so even though the
3185
+ // foreground overwrites `diag_stage` on inline recovery, the worker's last
3186
+ // reached point survives. If the last worker_stage is `model_done`, writes
3187
+ // hang right after model resolution (DB connection); if it's
3188
+ // `engine_resolved`/`systemprompt_enter`, the stall is in the named step.
3189
+ workerStep("engine_resolved");
3190
+ // DIAGNOSTIC-ONLY: AWAITED probe (bg worker only). worker_stage stalls at
3191
+ // `model_done` even though the code right after is trivial sync — this
3192
+ // distinguishes the two causes: if `post_model_awaited` lands, DB writes
3193
+ // still work after model_done and the stall is later in the main flow; if it
3194
+ // never lands (stays `model_done`), the bg-fn DB connection itself is hung
3195
+ // right after model resolution. `recordRunDiagnostic` is `withDbTimeout`-
3196
+ // bounded, so a hung write rejects (caught) rather than blocking forever.
3197
+ if (isBackgroundWorker && bgRunId) {
3198
+ const probeStart = Date.now();
3199
+ try {
3200
+ await recordRunDiagnostic(bgRunId, RUN_DIAG_STAGE.workerSetupStep, "post_model_awaited");
3201
+ workerStep(`post_model_ok=${Date.now() - probeStart}ms`);
3202
+ }
3203
+ catch (e) {
3204
+ workerStep(`post_model_threw=${Date.now() - probeStart}ms:${String(e?.message ?? e).slice(0, 80)}`);
3205
+ }
3206
+ }
3173
3207
  // One-line per-turn resolution log so it's obvious in dev which engine
3174
3208
  // is actually handling the request. `requestEngine` is what the client
3175
3209
  // sent from the model picker; `engine.name` is what resolveEngine picked.
@@ -3209,10 +3243,17 @@ export function createProductionAgentHandler(options) {
3209
3243
  let systemPromptError = null;
3210
3244
  const systemPromptThunk = () => (async () => {
3211
3245
  const sysPromptStart = Date.now();
3246
+ // Brackets the system-prompt build (which runs the template's
3247
+ // extraContext / data-dictionary). If worker_stage stalls at
3248
+ // `systemprompt_enter` with no `systemprompt_done`, the build itself is
3249
+ // the stall point.
3250
+ workerStep("systemprompt_enter");
3212
3251
  try {
3213
- return typeof options.systemPrompt === "function"
3252
+ const built = typeof options.systemPrompt === "function"
3214
3253
  ? await options.systemPrompt(event)
3215
3254
  : options.systemPrompt;
3255
+ workerStep("systemprompt_done");
3256
+ return built;
3216
3257
  }
3217
3258
  catch (error) {
3218
3259
  systemPromptError = error;
@@ -3923,6 +3964,21 @@ export function createProductionAgentHandler(options) {
3923
3964
  // DIAGNOSTIC-ONLY: last stage before startRun fires. A worker that reaches
3924
3965
  // prestart but never workerStarted is hanging inside startRun itself.
3925
3966
  workerStep("prestart");
3967
+ // DIAGNOSTIC-ONLY: AWAITED probe right before startRun (bg worker only). If
3968
+ // worker_stage reaches `pre_claim_ok` but the run never flips to
3969
+ // `worker_started`, the stall is inside startRun's claim itself; if it never
3970
+ // reaches `pre_claim_*`, the stall is somewhere in the setup between
3971
+ // post_model and here.
3972
+ if (isBackgroundWorker && bgRunId) {
3973
+ const claimProbeStart = Date.now();
3974
+ try {
3975
+ await recordRunDiagnostic(bgRunId, RUN_DIAG_STAGE.workerSetupStep, "pre_claim");
3976
+ workerStep(`pre_claim_ok=${Date.now() - claimProbeStart}ms`);
3977
+ }
3978
+ catch (e) {
3979
+ workerStep(`pre_claim_threw=${Date.now() - claimProbeStart}ms:${String(e?.message ?? e).slice(0, 80)}`);
3980
+ }
3981
+ }
3926
3982
  // DIAGNOSTIC-ONLY: peak-ish RSS (MB) + assembled system-prompt size (KB) at
3927
3983
  // prestart. The analytics bg worker dies right after model_done; if the
3928
3984
  // FOREGROUND (identical build, writes land) is already near the ~1024MB