@nextclaw/kernel 0.10.0 → 0.10.1

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/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { AUTOMATIC_UPDATE_CHECK_INTERVAL_MS, getAutomaticUpdateCheckDelay, resol
2
2
  import { n as getUnsignedUpdateManifest, r as serializeUnsignedUpdateManifest, t as UpdateManifestReader } from "./update-manifest.types-C0qPrjGQ.js";
3
3
  import { createRequire } from "node:module";
4
4
  import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOKENS, CONTEXT_COMPACTION_METADATA_KEY, ConfigSchema, ContextCompactionService, ContextWindowBudgetService, CronService, CronTool, DEFAULT_PANELS_DIR, DEFAULT_SERVICE_APPS_DIR, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_WORKSPACE_REPOSITORY_IDENTITY_RESOLVER, DIAGNOSTIC_CORRELATION_METADATA_KEY, DiagnosticRuntime, EditFileTool, ExecTool, ExtensionChannelAdapter, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, LiteLLMProvider, MAX_SESSION_SEARCH_LIMIT, MemoryGetTool, MemorySearchTool, MemoryStore, MessageBus, MessageTool, ProviderModelDiscoveryService, ProviderRegistry, ReadFileTool, RequestedSkillsMetadataReader, SILENT_REPLY_TOKEN, SessionProjectContextResolver, SessionSearchService, SkillsLoader, THINKING_LEVELS, ViewImageTool, WebFetchTool, WebSearchTool, WriteFileTool, buildCompressingCompactionCheckpoint, buildConfigSchema, buildContextWindowSnapshot, buildReloadPlan, buildSessionRequestToolResult, buildToolCatalogEntries, createAgentProfile, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createCompletedSessionRequest, createFailedSessionRequest, createRunningSessionRequest, createRuntimeChildEnv, createTypingStopControlMessage, diffConfigPaths, ensureDir, estimateInputTokens, evaluateSilentReply, expandHome, findEffectiveAgentProfile, getConfigPath, getDataDir, getDataPath, getSessionsPath, getWorkspacePath, getWorkspacePathFromConfig, isNextclawControlMessage, loadConfig, mergeExtensionConfigView, modelSupportsVision, normalizeAgentProfileId, normalizeInlineSecretRefs, normalizeModelThinkingCapability, normalizeProviderModelConfig, normalizeToolParams, parseAgentScopedSessionKey, parseThinkingLevel, readCompressedContextCompactionCheckpoint, readOptionalString, readParentSessionId, readSessionProjectRoot, redactConfigObject, removeAgentProfile, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveEffectiveAgentProfiles, resolveNextclawSelfManageGuidePaths, resolveProviderRuntime, resolveRuntimeCommandLaunch, resolveSessionProjectContext, resolveSessionWorkspacePath, resolveThinkingLevel, safeFilename, sanitizeOutboundAssistantContent, saveConfig, summarizeSessionRequestTask, toExtensionConfigView, updateAgentProfile } from "@nextclaw/core";
5
- import { NCP_AI_EXECUTION_METADATA_KEY, NCP_INTERNAL_VISIBILITY_METADATA_KEY, NcpEventType, OBSERVATION_EVENT_EXTENSION_TYPE, createUnavailableNcpAiExecutionMetadata, isHiddenNcpMessage, normalizeAssistantText, readNcpAiExecutionMetadata, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
5
+ import { NCP_AI_EXECUTION_METADATA_KEY, NCP_INTERNAL_VISIBILITY_METADATA_KEY, NCP_RUN_TRIGGER_METADATA_KEY, NcpEventType, OBSERVATION_EVENT_EXTENSION_TYPE, createUnavailableNcpAiExecutionMetadata, isHiddenNcpMessage, normalizeAssistantText, parseNcpRunTriggerInput, readNcpAiExecutionMetadata, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
6
6
  import { CHAT_CONTINUATION_TARGET_MESSAGE_METADATA_KEY, CHAT_CONVERSATION_EXCERPT_TOKEN_KIND, CHAT_INLINE_TOKENS_METADATA_KEY, CHAT_INLINE_TOKENS_SCHEMA_VERSION, CHAT_PROJECT_TOKEN_KIND, CHAT_SESSION_MATERIALIZATION_METADATA_KEY, CHAT_SYSTEM_OBJECT_TOKEN_KIND, CHAT_UI_RESOURCE_TOKEN_KIND, CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND, CHAT_WORKSPACE_EXCERPT_TOKEN_KIND, CHAT_WORKSPACE_FILE_TOKEN_KIND, Contribution as Contribution$1, EventBus, EventBus as EventBus$1, Ingress, Ingress as Ingress$1, PANEL_APP_INLINE_HOST_CONTRACT, PANEL_APP_SCROLL_RESTORATION_CONTRACT, SYSTEM_OBJECT_REFERENCE_DEFAULT_LIMIT, SYSTEM_OBJECT_REFERENCE_MAX_LIMIT, SYSTEM_OBJECT_TYPE_CRON_JOB, SYSTEM_OBJECT_TYPE_INBOX_DELIVERY, UI_CONTENT_PARAMS_HOST_CONTRACT, classifyDiagnosticError, createSystemObjectReferenceUri, createTypedKey, eventKeys, eventKeys as eventKeys$1, ingressKeys, ingressKeys as ingressKeys$1, isRuntimeDefaultModelValue, isSilentReplyNcpMessage, normalizeRuntimeModelSelectionMode, parseSystemObjectReferenceUri, readChatUiResourceReference, readInlineContentHeight, readSystemObjectResolvedReference, readUiContentParams } from "@nextclaw/shared";
7
7
  import { LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, isTextLikeAsset, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
8
8
  import { createHash, createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
@@ -1514,26 +1514,26 @@ const SESSION_ACTIVITY_PREVIEW_STATUS_KINDS = new Set([
1514
1514
  "run-failed",
1515
1515
  "run-interrupted"
1516
1516
  ]);
1517
- function isRecord$18(value) {
1517
+ function isRecord$19(value) {
1518
1518
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1519
1519
  }
1520
- function readOptionalString$12(value) {
1520
+ function readOptionalString$13(value) {
1521
1521
  if (typeof value !== "string") return;
1522
1522
  const trimmed = value.trim();
1523
1523
  return trimmed.length > 0 ? trimmed : void 0;
1524
1524
  }
1525
1525
  function readSessionActivityPreviewMetadata(value) {
1526
- if (!isRecord$18(value)) return null;
1526
+ if (!isRecord$19(value)) return null;
1527
1527
  const state = value.state;
1528
- const timestamp = readOptionalString$12(value.timestamp);
1528
+ const timestamp = readOptionalString$13(value.timestamp);
1529
1529
  if (!SESSION_ACTIVITY_PREVIEW_STATES.has(state) || !timestamp) return null;
1530
- const statusKind = readOptionalString$12(value.statusKind);
1530
+ const statusKind = readOptionalString$13(value.statusKind);
1531
1531
  return {
1532
1532
  state,
1533
1533
  timestamp,
1534
1534
  statusKind: SESSION_ACTIVITY_PREVIEW_STATUS_KINDS.has(statusKind) ? statusKind : void 0,
1535
- statusText: readOptionalString$12(value.statusText),
1536
- replyText: readOptionalString$12(value.replyText)
1535
+ statusText: readOptionalString$13(value.statusText),
1536
+ replyText: readOptionalString$13(value.replyText)
1537
1537
  };
1538
1538
  }
1539
1539
  function compareIsoTimestamp(left, right) {
@@ -1617,7 +1617,7 @@ var SessionActivityPreviewEventService = class {
1617
1617
  //#endregion
1618
1618
  //#region src/managers/agent-run-session-command.manager.ts
1619
1619
  const CONTINUATION_PROMPT = "Continue from where you stopped. Preserve completed work and avoid repeating it.";
1620
- function isRecord$17(value) {
1620
+ function isRecord$18(value) {
1621
1621
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1622
1622
  }
1623
1623
  function hasSendableMessageContent(message) {
@@ -1625,7 +1625,7 @@ function hasSendableMessageContent(message) {
1625
1625
  }
1626
1626
  function readSessionActivityState(metadata) {
1627
1627
  const preview = metadata?.[SESSION_ACTIVITY_PREVIEW_METADATA_KEY];
1628
- return isRecord$17(preview) && typeof preview.state === "string" ? preview.state : null;
1628
+ return isRecord$18(preview) && typeof preview.state === "string" ? preview.state : null;
1629
1629
  }
1630
1630
  var AgentRunSessionCommandManager = class {
1631
1631
  pendingCommands = /* @__PURE__ */ new Map();
@@ -1676,6 +1676,13 @@ var AgentRunSessionCommandManager = class {
1676
1676
  role: "user",
1677
1677
  status: "final",
1678
1678
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1679
+ },
1680
+ trigger: {
1681
+ actor: "human",
1682
+ source: "message-edit",
1683
+ triggeredAt: (/* @__PURE__ */ new Date()).toISOString(),
1684
+ sourceSessionId: request.sessionId,
1685
+ sourceMessageId: request.messageId
1679
1686
  }
1680
1687
  });
1681
1688
  };
@@ -1685,6 +1692,7 @@ var AgentRunSessionCommandManager = class {
1685
1692
  const activityState = readSessionActivityState(sourceRecord.metadata);
1686
1693
  if (activityState !== "cancelled" && activityState !== "failed") throw new Error("Only a cancelled or failed session can continue running.");
1687
1694
  const latestVisibleConversationMessage = [...sourceRecord.messages].reverse().find((message) => (message.role === "user" || message.role === "assistant") && !isHiddenNcpMessage(message) && !isSilentReplyNcpMessage(message));
1695
+ const triggeredAt = (/* @__PURE__ */ new Date()).toISOString();
1688
1696
  return await this.send({
1689
1697
  correlationId: request.correlationId,
1690
1698
  sessionId: request.sessionId,
@@ -1693,7 +1701,7 @@ var AgentRunSessionCommandManager = class {
1693
1701
  sessionId: request.sessionId,
1694
1702
  role: "user",
1695
1703
  status: "final",
1696
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1704
+ timestamp: triggeredAt,
1697
1705
  parts: [{
1698
1706
  type: "text",
1699
1707
  text: CONTINUATION_PROMPT
@@ -1702,6 +1710,13 @@ var AgentRunSessionCommandManager = class {
1702
1710
  [NCP_INTERNAL_VISIBILITY_METADATA_KEY]: "hidden",
1703
1711
  [CHAT_CONTINUATION_TARGET_MESSAGE_METADATA_KEY]: latestVisibleConversationMessage?.role === "assistant" ? latestVisibleConversationMessage.id : void 0
1704
1712
  }
1713
+ },
1714
+ trigger: {
1715
+ actor: "human",
1716
+ source: "continue-run",
1717
+ triggeredAt,
1718
+ sourceSessionId: request.sessionId,
1719
+ ...latestVisibleConversationMessage ? { sourceMessageId: latestVisibleConversationMessage.id } : {}
1705
1720
  }
1706
1721
  });
1707
1722
  };
@@ -1950,13 +1965,13 @@ function createUnavailableAiExecutionMetadataEvent(params) {
1950
1965
  }
1951
1966
  //#endregion
1952
1967
  //#region src/utils/agent-run-request.utils.ts
1953
- function readOptionalString$11(value) {
1968
+ function readOptionalString$12(value) {
1954
1969
  if (typeof value !== "string") return;
1955
1970
  return value.trim() || void 0;
1956
1971
  }
1957
1972
  function toAgentRunRequest(envelope) {
1958
1973
  const metadata = envelope.metadata ?? {};
1959
- const peerId = readOptionalString$11(envelope.peerId);
1974
+ const peerId = readOptionalString$12(envelope.peerId);
1960
1975
  const requestMetadata = {
1961
1976
  agentRuntimeId: metadata.agentRuntimeId,
1962
1977
  agentId: metadata.agentId,
@@ -1968,10 +1983,10 @@ function toAgentRunRequest(envelope) {
1968
1983
  maxTokens: metadata.maxTokens,
1969
1984
  thinkingEffort: metadata.thinkingEffort,
1970
1985
  delivery: envelope.delivery,
1971
- idempotencyKey: readOptionalString$11(envelope.idempotencyKey)
1986
+ idempotencyKey: readOptionalString$12(envelope.idempotencyKey)
1972
1987
  };
1973
1988
  if (Array.isArray(envelope.content)) {
1974
- const sessionId = readOptionalString$11(envelope.sessionId);
1989
+ const sessionId = readOptionalString$12(envelope.sessionId);
1975
1990
  if (sessionId && peerId) throw new Error("agent-run.send cannot accept both sessionId and peerId.");
1976
1991
  return {
1977
1992
  ...requestMetadata,
@@ -1989,8 +2004,8 @@ function toAgentRunRequest(envelope) {
1989
2004
  }
1990
2005
  const sourceMessage = envelope.message;
1991
2006
  if (!sourceMessage) throw new Error("Invalid agent run send request.");
1992
- const envelopeSessionId = readOptionalString$11(envelope.sessionId);
1993
- const messageSessionId = readOptionalString$11(sourceMessage.sessionId);
2007
+ const envelopeSessionId = readOptionalString$12(envelope.sessionId);
2008
+ const messageSessionId = readOptionalString$12(sourceMessage.sessionId);
1994
2009
  const sessionId = envelopeSessionId ?? messageSessionId;
1995
2010
  if (sessionId && peerId) throw new Error("agent-run.send cannot accept both sessionId and peerId.");
1996
2011
  return {
@@ -2051,7 +2066,7 @@ function readSessionMaterialization(metadata) {
2051
2066
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2052
2067
  const materialization = value;
2053
2068
  if (materialization.kind !== "child") throw new Error("session_materialization.kind must be \"child\".");
2054
- const parentSessionId = readOptionalString$11(materialization.parentSessionId);
2069
+ const parentSessionId = readOptionalString$12(materialization.parentSessionId);
2055
2070
  if (!parentSessionId) throw new Error("session_materialization.parentSessionId is required.");
2056
2071
  if (materialization.inheritContext !== true) throw new Error("session_materialization.inheritContext must be true.");
2057
2072
  return {
@@ -2093,7 +2108,7 @@ function resolveRunSpec(params) {
2093
2108
  };
2094
2109
  }
2095
2110
  function attachRunSpecMetadata(params) {
2096
- const { message, modelSource, request, session, spec, startedAt } = params;
2111
+ const { message, modelSource, request, session, spec, startedAt, trigger } = params;
2097
2112
  const metadata = structuredClone(message.metadata ?? {});
2098
2113
  metadata[AGENT_RUN_MESSAGE_RUN_SPEC_METADATA_KEY] = {
2099
2114
  version: 1,
@@ -2112,6 +2127,7 @@ function attachRunSpecMetadata(params) {
2112
2127
  correlationId: spec.correlationId ?? null,
2113
2128
  execution: structuredClone(AGENT_RUN_EXECUTION_METADATA)
2114
2129
  };
2130
+ metadata[NCP_RUN_TRIGGER_METADATA_KEY] = structuredClone(trigger);
2115
2131
  return {
2116
2132
  ...message,
2117
2133
  metadata
@@ -2164,7 +2180,7 @@ var AgentRuntimeRunObserverService = class {
2164
2180
  }));
2165
2181
  messageCompletedSeen = true;
2166
2182
  }
2167
- eventsToPublish.push(event);
2183
+ eventsToPublish.push(this.projectCompletedMessage(event, sessionRun));
2168
2184
  eventsToPublish.forEach(this.publishNcpEvent);
2169
2185
  if (consumedSteeringInput) this.publishRunQueueUpdated(session.sessionId);
2170
2186
  }), catchError(async (error) => {
@@ -2235,6 +2251,17 @@ var AgentRuntimeRunObserverService = class {
2235
2251
  source: "agent-run-request"
2236
2252
  });
2237
2253
  };
2254
+ projectCompletedMessage = (event, sessionRun) => {
2255
+ if (event.type !== NcpEventType.MessageCompleted) return event;
2256
+ const message = sessionRun.getSnapshot().messages.find((item) => item.id === event.payload.message.id);
2257
+ return message ? {
2258
+ ...event,
2259
+ payload: {
2260
+ ...event.payload,
2261
+ message: structuredClone(message)
2262
+ }
2263
+ } : event;
2264
+ };
2238
2265
  publishNcpEvent = (event) => {
2239
2266
  this.options.eventBus.emit(eventKeys$1.ncpEvent, event, {
2240
2267
  emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2243,6 +2270,110 @@ var AgentRuntimeRunObserverService = class {
2243
2270
  };
2244
2271
  };
2245
2272
  //#endregion
2273
+ //#region src/utils/agent-run-trigger.utils.ts
2274
+ function isRecord$17(value) {
2275
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2276
+ }
2277
+ function readOptionalString$11(value) {
2278
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
2279
+ }
2280
+ function readSourceRunSpec(message) {
2281
+ const value = message.metadata?.[AGENT_RUN_MESSAGE_RUN_SPEC_METADATA_KEY];
2282
+ return isRecord$17(value) ? value : {};
2283
+ }
2284
+ const SOURCE_CONTEXT_KEYS = [
2285
+ "channel",
2286
+ "chatId",
2287
+ "accountId",
2288
+ "senderId",
2289
+ "sessionKey",
2290
+ "cron_job_id",
2291
+ "cron_job_name",
2292
+ "session_origin",
2293
+ "observation_delivery_id",
2294
+ "observation_subscription_id"
2295
+ ];
2296
+ function readSourceContext(request) {
2297
+ const candidates = {
2298
+ ...request.metadata ?? {},
2299
+ ...request.message.metadata ?? {},
2300
+ ...request.peerId ? { peerId: request.peerId } : {}
2301
+ };
2302
+ const entries = [...SOURCE_CONTEXT_KEYS, "peerId"].flatMap((key) => {
2303
+ const value = candidates[key];
2304
+ return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value) || typeof value === "boolean" ? [[key, value]] : [];
2305
+ });
2306
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
2307
+ }
2308
+ function createAgentToolRunTriggerInput(params) {
2309
+ const { request, source } = params;
2310
+ const runSpec = readSourceRunSpec(request.message);
2311
+ const sourceSessionId = readOptionalString$11(request.sessionId);
2312
+ const sourceRunId = readOptionalString$11(runSpec.runId);
2313
+ const sourceModel = readOptionalString$11(runSpec.model);
2314
+ const sourceContext = readSourceContext(request);
2315
+ return {
2316
+ actor: "agent",
2317
+ source,
2318
+ triggeredAt: (/* @__PURE__ */ new Date()).toISOString(),
2319
+ ...sourceSessionId ? { sourceSessionId } : {},
2320
+ sourceMessageId: request.message.id,
2321
+ ...sourceRunId ? { sourceRunId } : {},
2322
+ ...sourceModel ? { sourceModel } : {},
2323
+ ...sourceContext ? { sourceContext } : {}
2324
+ };
2325
+ }
2326
+ function createIngressRunTriggerInput(params) {
2327
+ const { request } = params;
2328
+ const source = request.channel ? `channel:${request.channel}` : params.source.trim() || "internal";
2329
+ const sourceSessionId = readOptionalString$11(request.sessionId);
2330
+ const sourceContext = readSourceContext(request);
2331
+ const cronJobId = readOptionalString$11(request.metadata?.cron_job_id);
2332
+ const isCron = request.metadata?.session_origin === "cron" || Boolean(cronJobId);
2333
+ return {
2334
+ actor: isCron ? "automation" : request.message.role === "user" ? "human" : request.message.role === "service" ? "automation" : "system",
2335
+ source: isCron ? cronJobId ? `cron:${cronJobId}` : "cron" : source,
2336
+ triggeredAt: request.message.timestamp,
2337
+ ...sourceSessionId ? { sourceSessionId } : {},
2338
+ sourceMessageId: request.message.id,
2339
+ ...sourceContext ? { sourceContext } : {}
2340
+ };
2341
+ }
2342
+ function resolveRunTriggerMetadata(params) {
2343
+ const { request, spec, startedAt } = params;
2344
+ const input = request.trigger ?? createIngressRunTriggerInput({
2345
+ request,
2346
+ source: "internal"
2347
+ });
2348
+ const triggeredAt = Number.isFinite(Date.parse(input.triggeredAt)) ? new Date(input.triggeredAt).toISOString() : startedAt;
2349
+ return {
2350
+ version: 1,
2351
+ ...structuredClone(input),
2352
+ triggeredAt,
2353
+ targetRunId: spec.runId
2354
+ };
2355
+ }
2356
+ function createRunTriggerMetadataEvent(params) {
2357
+ const { sessionId, spec, trigger } = params;
2358
+ return {
2359
+ occurredAt: trigger.triggeredAt,
2360
+ type: NcpEventType.RunMetadata,
2361
+ payload: {
2362
+ sessionId,
2363
+ runId: spec.runId,
2364
+ correlationId: spec.correlationId,
2365
+ metadata: { [NCP_RUN_TRIGGER_METADATA_KEY]: structuredClone(trigger) }
2366
+ }
2367
+ };
2368
+ }
2369
+ function attachSourceToolCall(trigger, sourceToolCallId) {
2370
+ const normalizedToolCallId = readOptionalString$11(sourceToolCallId);
2371
+ return {
2372
+ ...structuredClone(trigger),
2373
+ ...normalizedToolCallId ? { sourceToolCallId: normalizedToolCallId } : {}
2374
+ };
2375
+ }
2376
+ //#endregion
2246
2377
  //#region src/managers/agent-run-request.manager.ts
2247
2378
  var AgentRunRequestManager = class {
2248
2379
  cleanups = [];
@@ -2283,9 +2414,14 @@ var AgentRunRequestManager = class {
2283
2414
  this.sessionCommandManager.dispose();
2284
2415
  this.started = false;
2285
2416
  };
2286
- handleSendRequest = async (envelope) => {
2417
+ handleSendRequest = async (envelope, context) => {
2287
2418
  if (!envelope.payload) throw new Error("Invalid agent run send request.");
2288
- return toRunHandle(await this.send(toAgentRunRequest(envelope.payload)));
2419
+ const request = toAgentRunRequest(envelope.payload);
2420
+ request.trigger = createIngressRunTriggerInput({
2421
+ request,
2422
+ source: context.source
2423
+ });
2424
+ return toRunHandle(await this.send(request));
2289
2425
  };
2290
2426
  handleAbortRequest = async (envelope) => {
2291
2427
  if (!envelope.payload?.sessionId) throw new Error("Invalid agent run abort request.");
@@ -2312,7 +2448,8 @@ var AgentRunRequestManager = class {
2312
2448
  ...envelope.payload.message,
2313
2449
  sessionId: envelope.payload.sessionId
2314
2450
  },
2315
- correlationId: envelope.payload.requestId
2451
+ correlationId: envelope.payload.requestId,
2452
+ trigger: structuredClone(envelope.payload.trigger)
2316
2453
  }));
2317
2454
  };
2318
2455
  send = async (request) => await this.idempotency.accept(request, this.sendOnce);
@@ -2365,6 +2502,11 @@ var AgentRunRequestManager = class {
2365
2502
  runId: activeRequest.runId,
2366
2503
  session
2367
2504
  });
2505
+ const trigger = resolveRunTriggerMetadata({
2506
+ request,
2507
+ spec,
2508
+ startedAt: requestRunStartedAt
2509
+ });
2368
2510
  const message = attachRunSpecMetadata({
2369
2511
  message: {
2370
2512
  ...request.message,
@@ -2375,7 +2517,8 @@ var AgentRunRequestManager = class {
2375
2517
  request,
2376
2518
  session,
2377
2519
  spec,
2378
- startedAt: requestRunStartedAt
2520
+ startedAt: requestRunStartedAt,
2521
+ trigger
2379
2522
  });
2380
2523
  const providerRequest = {
2381
2524
  ...request,
@@ -2402,8 +2545,14 @@ var AgentRunRequestManager = class {
2402
2545
  message,
2403
2546
  correlationId: request.correlationId
2404
2547
  });
2405
- await sessionRun.applyEvents([messageSentEvent]);
2548
+ const triggerEvent = createRunTriggerMetadataEvent({
2549
+ sessionId: session.sessionId,
2550
+ spec,
2551
+ trigger
2552
+ });
2553
+ await sessionRun.applyEvents([messageSentEvent, triggerEvent]);
2406
2554
  this.publishNcpEvent(messageSentEvent);
2555
+ this.publishNcpEvent(triggerEvent);
2407
2556
  try {
2408
2557
  const runtime = this.agentRuntimeManager.getOrCreate({
2409
2558
  agentRuntimeId: session.agentRuntimeId,
@@ -10237,20 +10386,39 @@ function readMessageFromSummaryEvent(event) {
10237
10386
  function readRecordLabel(record) {
10238
10387
  return readOptionalString(record.metadata?.label) ?? void 0;
10239
10388
  }
10389
+ function resolveRequestTrigger(params) {
10390
+ return {
10391
+ ...params.trigger ?? {
10392
+ actor: "system",
10393
+ source: "session-request",
10394
+ triggeredAt: (/* @__PURE__ */ new Date()).toISOString(),
10395
+ sourceSessionId: params.sourceSessionId
10396
+ },
10397
+ sourceRequestId: params.requestId
10398
+ };
10399
+ }
10240
10400
  var SessionRequestManager = class {
10241
10401
  constructor(options) {
10242
10402
  this.options = options;
10243
10403
  }
10244
10404
  spawnSessionAndRequest = async (params) => {
10245
- const { sourceSessionId, sourceToolCallId, updateToolCallResult, sourceSessionMetadata, metadataOverrides, contextInheritance, task, title, model, runtime, handoffDepth, sessionType, thinkingLevel, projectRoot, agentId, parentSessionId, notify } = params;
10405
+ const { sourceSessionId, sourceToolCallId, updateToolCallResult, sourceSessionMetadata, metadataOverrides, contextInheritance, task, title, model, runtime, handoffDepth, sessionType, thinkingLevel, projectRoot, agentId, parentSessionId, notify, trigger: requestedTrigger } = params;
10246
10406
  const requestId = randomUUID();
10407
+ const trigger = resolveRequestTrigger({
10408
+ trigger: requestedTrigger,
10409
+ sourceSessionId,
10410
+ requestId
10411
+ });
10247
10412
  const createdSession = await this.options.sessionManager.createSession({
10248
10413
  sourceSessionId,
10249
10414
  ...parentSessionId ? { parentSessionId } : {},
10250
10415
  task,
10251
10416
  title,
10252
10417
  sourceSessionMetadata,
10253
- ...metadataOverrides ? { metadataOverrides } : {},
10418
+ metadataOverrides: {
10419
+ ...metadataOverrides ?? {},
10420
+ session_creation_trigger: structuredClone(trigger)
10421
+ },
10254
10422
  contextInheritance,
10255
10423
  agentId,
10256
10424
  model,
@@ -10273,18 +10441,25 @@ var SessionRequestManager = class {
10273
10441
  agentId: createdSession.agentId,
10274
10442
  isChildSession: Boolean(parentSessionId),
10275
10443
  ...parentSessionId ? { parentSessionId } : {},
10276
- spawnedByRequestId: requestId
10444
+ spawnedByRequestId: requestId,
10445
+ trigger
10277
10446
  });
10278
10447
  };
10279
10448
  requestSession = async (params) => {
10280
- const { sourceSessionId, sourceToolCallId, updateToolCallResult, targetSessionId, task, title, notify, handoffDepth } = params;
10449
+ const { sourceSessionId, sourceToolCallId, updateToolCallResult, targetSessionId, task, title, notify, handoffDepth, trigger: requestedTrigger } = params;
10281
10450
  const normalizedTargetSessionId = targetSessionId.trim();
10282
10451
  if (normalizedTargetSessionId === sourceSessionId.trim()) throw new Error("sessions_request cannot target the current session.");
10283
10452
  const targetSession = await this.options.sessionManager.getSessionRecord(normalizedTargetSessionId);
10284
10453
  if (!targetSession) throw new Error(`Target session not found: ${targetSessionId}`);
10285
10454
  const parentSessionId = readParentSessionId(targetSession.metadata);
10455
+ const requestId = randomUUID();
10456
+ const trigger = resolveRequestTrigger({
10457
+ trigger: requestedTrigger,
10458
+ sourceSessionId,
10459
+ requestId
10460
+ });
10286
10461
  return this.dispatchRequest({
10287
- requestId: randomUUID(),
10462
+ requestId,
10288
10463
  sourceSessionId,
10289
10464
  sourceToolCallId,
10290
10465
  updateToolCallResult,
@@ -10296,11 +10471,12 @@ var SessionRequestManager = class {
10296
10471
  agentId: targetSession.agentId,
10297
10472
  isChildSession: Boolean(parentSessionId),
10298
10473
  parentSessionId: parentSessionId ?? void 0,
10299
- spawnedByRequestId: void 0
10474
+ spawnedByRequestId: void 0,
10475
+ trigger
10300
10476
  });
10301
10477
  };
10302
10478
  dispatchRequest = async (params) => {
10303
- const { requestId, sourceSessionId, sourceToolCallId, updateToolCallResult, targetSessionId, task, title, handoffDepth, notify, agentId, isChildSession, parentSessionId, spawnedByRequestId } = params;
10479
+ const { requestId, sourceSessionId, sourceToolCallId, updateToolCallResult, targetSessionId, task, title, handoffDepth, notify, agentId, isChildSession, parentSessionId, spawnedByRequestId, trigger } = params;
10304
10480
  const request = createRunningSessionRequest({
10305
10481
  requestId,
10306
10482
  sourceSessionId,
@@ -10311,7 +10487,8 @@ var SessionRequestManager = class {
10311
10487
  title,
10312
10488
  task,
10313
10489
  isChildSession,
10314
- parentSessionId
10490
+ parentSessionId,
10491
+ trigger
10315
10492
  });
10316
10493
  const resultContext = {
10317
10494
  task,
@@ -10469,6 +10646,13 @@ function waitForAgentRuntimeSessionReply(input) {
10469
10646
  };
10470
10647
  }
10471
10648
  async function dispatchAgentRuntimeSessionRequest(input) {
10649
+ const trigger = parseNcpRunTriggerInput(input.request.metadata?.run_trigger) ?? {
10650
+ actor: "system",
10651
+ source: "session-request-recovery",
10652
+ triggeredAt: input.request.createdAt,
10653
+ sourceSessionId: input.request.sourceSessionId,
10654
+ sourceRequestId: input.request.requestId
10655
+ };
10472
10656
  await input.ingress.handle({
10473
10657
  type: ingressKeys$1.agentRun.sessionMessageRequest,
10474
10658
  payload: {
@@ -10485,7 +10669,8 @@ async function dispatchAgentRuntimeSessionRequest(input) {
10485
10669
  metadata: { session_request_id: input.request.requestId }
10486
10670
  },
10487
10671
  requestId: input.request.requestId,
10488
- sessionId: input.request.targetSessionId
10672
+ sessionId: input.request.targetSessionId,
10673
+ trigger: structuredClone(trigger)
10489
10674
  }
10490
10675
  }, { source: "session-request" });
10491
10676
  }
@@ -12826,6 +13011,7 @@ const createSelfManagementContextProvider = () => ({ provide: () => {
12826
13011
  } });
12827
13012
  const createSessionOrchestrationContextProvider = () => staticBlock([
12828
13013
  "## Session Orchestration",
13014
+ "- Only top-level sessions can create new sessions. Child sessions must complete their delegated task directly and return further delegation needs to the parent session.",
12829
13015
  "- Before passing a non-default `runtime` to `sessions_spawn` or agent creation/update flows, inspect the installed runtime kinds with `nextclaw agents runtimes --json`.",
12830
13016
  "- `sessions_spawn` is the unified session-creation tool. Omit `scope` or use `scope=\"standalone\"` for a regular session, and use `scope=\"child\"` when the new session should be a child session of the current flow.",
12831
13017
  "- `sessions_spawn` only creates the session by default. Add top-level `notify: \"none\" | \"final_reply\"` when the new session should start working immediately.",
@@ -12977,6 +13163,9 @@ function renderWebSearchReadiness(params) {
12977
13163
  if (!readSearchApiKey(searchConfig, provider)) return `web_search is not ready: provider ${provider} has no API key configured.`;
12978
13164
  return `web_search is ready with provider ${provider}.`;
12979
13165
  }
13166
+ function renderDelegationGuidance(toolNames) {
13167
+ return toolNames.includes("sessions_spawn") ? "If a task is more complex or takes longer, spawn a sub-agent. Completion is push-based: it will auto-announce when done." : "Sub-agent spawning is unavailable in this delegated session. Complete the assigned task directly and return any further delegation needs to the parent session.";
13168
+ }
12980
13169
  var ToolingContextProvider = class {
12981
13170
  constructor(context) {
12982
13171
  this.context = context;
@@ -12998,7 +13187,7 @@ var ToolingContextProvider = class {
12998
13187
  "TOOLS.md does not control tool availability; it is user guidance for how to use external tools.",
12999
13188
  "For long waits, avoid rapid poll loops: use exec with enough yieldMs.",
13000
13189
  "For relative time/date scheduling requests (for example 'in 5 minutes' / '1分钟后'), first check the current local time with an available tool such as exec/date, then convert it to an absolute ISO time with timezone. Do not guess.",
13001
- "If a task is more complex or takes longer, spawn a sub-agent. Completion is push-based: it will auto-announce when done.",
13190
+ renderDelegationGuidance(toolCatalog.map((tool) => tool.name)),
13002
13191
  "Do not poll `subagents list` / `sessions_list` in a loop; only check status on-demand (for intervention, debugging, or when explicitly asked)."
13003
13192
  ].join("\n")];
13004
13193
  };
@@ -13692,6 +13881,7 @@ var LearningLoopContribution = class extends Contribution$1 {
13692
13881
  if (toolCallsSinceReview < runtimeConfig.toolCallThreshold) return;
13693
13882
  this.inFlightSessionIds.add(sessionId);
13694
13883
  try {
13884
+ const triggeredAt = (/* @__PURE__ */ new Date()).toISOString();
13695
13885
  const reviewSession = await this.sessionRequester.spawnSessionAndRequest({
13696
13886
  sourceSessionId: sessionId,
13697
13887
  sourceSessionMetadata: metadata,
@@ -13707,11 +13897,18 @@ var LearningLoopContribution = class extends Contribution$1 {
13707
13897
  sessionId,
13708
13898
  toolCallsSinceReview,
13709
13899
  currentToolCallCount: totalToolCalls
13710
- })
13900
+ }),
13901
+ trigger: {
13902
+ actor: "automation",
13903
+ source: "learning-loop",
13904
+ triggeredAt,
13905
+ sourceSessionId: sessionId,
13906
+ ...typeof metadata.preferred_model === "string" && metadata.preferred_model.trim() ? { sourceModel: metadata.preferred_model.trim() } : {}
13907
+ }
13711
13908
  });
13712
13909
  await this.sessionStore.updateSessionMetadata(sessionId, {
13713
13910
  [LEARNING_LOOP_LAST_TOOL_CALL_COUNT_METADATA_KEY]: totalToolCalls,
13714
- [LEARNING_LOOP_LAST_REQUESTED_AT_METADATA_KEY]: (/* @__PURE__ */ new Date()).toISOString(),
13911
+ [LEARNING_LOOP_LAST_REQUESTED_AT_METADATA_KEY]: triggeredAt,
13715
13912
  [LEARNING_LOOP_LAST_REVIEW_SESSION_ID_METADATA_KEY]: reviewSession.sessionId
13716
13913
  });
13717
13914
  } finally {
@@ -14076,12 +14273,14 @@ var SessionRequestTool = class {
14076
14273
  };
14077
14274
  sourceSessionId = "";
14078
14275
  handoffDepth = 0;
14276
+ trigger = null;
14079
14277
  constructor(manager) {
14080
14278
  this.manager = manager;
14081
14279
  }
14082
14280
  setContext = (params) => {
14083
14281
  this.sourceSessionId = params.sourceSessionId;
14084
14282
  this.handoffDepth = params.handoffDepth ?? 0;
14283
+ this.trigger = structuredClone(params.trigger);
14085
14284
  };
14086
14285
  execute = async (args, context) => {
14087
14286
  const params = normalizeToolParams(args);
@@ -14098,9 +14297,14 @@ var SessionRequestTool = class {
14098
14297
  task,
14099
14298
  title: readOptionalString$6(params, "title"),
14100
14299
  notify: notifyMode,
14101
- handoffDepth: this.handoffDepth
14300
+ handoffDepth: this.handoffDepth,
14301
+ trigger: attachSourceToolCall(this.readTriggerOrThrow(), context?.toolCallId)
14102
14302
  });
14103
14303
  };
14304
+ readTriggerOrThrow = () => {
14305
+ if (!this.trigger) throw new Error("sessions_request requires an active run trigger context.");
14306
+ return structuredClone(this.trigger);
14307
+ };
14104
14308
  };
14105
14309
  //#endregion
14106
14310
  //#region src/tools/session-search.tools.ts
@@ -14240,16 +14444,20 @@ var SessionSpawnTool = class {
14240
14444
  sourceSessionId = "";
14241
14445
  sourceSessionMetadata = {};
14242
14446
  handoffDepth = 0;
14447
+ trigger = null;
14243
14448
  constructor(sessionManager, sessionRequestManager) {
14244
14449
  this.sessionManager = sessionManager;
14245
14450
  this.sessionRequestManager = sessionRequestManager;
14246
14451
  }
14247
14452
  setContext = (params) => {
14248
- this.sourceSessionId = params.sourceSessionId;
14249
- this.sourceSessionMetadata = structuredClone(params.sourceSessionMetadata);
14250
- this.handoffDepth = params.handoffDepth ?? 0;
14453
+ const { handoffDepth, sourceSessionId, sourceSessionMetadata, trigger } = params;
14454
+ this.sourceSessionId = sourceSessionId;
14455
+ this.sourceSessionMetadata = structuredClone(sourceSessionMetadata);
14456
+ this.handoffDepth = handoffDepth ?? 0;
14457
+ this.trigger = structuredClone(trigger);
14251
14458
  };
14252
14459
  execute = async (args, context) => {
14460
+ const { toolCallId, updateToolCallResult } = context ?? {};
14253
14461
  const { agentId: rawAgentId, model: rawModel, notify: rawNotify, runtime: rawRuntime, scope: rawScope, task: rawTask, title: rawTitle, inheritContext: rawInheritContext } = normalizeToolParams(args);
14254
14462
  const task = readRequiredString$4(rawTask, "task");
14255
14463
  const scope = readSpawnScope(rawScope);
@@ -14257,11 +14465,12 @@ var SessionSpawnTool = class {
14257
14465
  const inheritContext = readInheritContext(rawInheritContext);
14258
14466
  if (inheritContext && scope !== "child") throw new Error("inheritContext=true requires scope=\"child\".");
14259
14467
  const parentSessionId = scope === "child" ? this.readParentSessionIdOrThrow() : void 0;
14260
- const contextInheritance = inheritContext ? { anchorToolCallId: context?.toolCallId } : void 0;
14468
+ const contextInheritance = inheritContext ? { anchorToolCallId: toolCallId } : void 0;
14469
+ const trigger = attachSourceToolCall(this.readTriggerOrThrow(), toolCallId);
14261
14470
  if (notify) return this.sessionRequestManager.spawnSessionAndRequest({
14262
14471
  sourceSessionId: this.sourceSessionId,
14263
- sourceToolCallId: context?.toolCallId,
14264
- updateToolCallResult: context?.updateToolCallResult,
14472
+ sourceToolCallId: toolCallId,
14473
+ updateToolCallResult,
14265
14474
  sourceSessionMetadata: this.sourceSessionMetadata,
14266
14475
  task,
14267
14476
  title: readOptionalString$5(rawTitle),
@@ -14271,7 +14480,8 @@ var SessionSpawnTool = class {
14271
14480
  contextInheritance,
14272
14481
  handoffDepth: this.handoffDepth,
14273
14482
  parentSessionId,
14274
- notify
14483
+ notify,
14484
+ trigger
14275
14485
  });
14276
14486
  const session = await this.sessionManager.createSession({
14277
14487
  sourceSessionId: this.sourceSessionId,
@@ -14282,7 +14492,8 @@ var SessionSpawnTool = class {
14282
14492
  model: readOptionalString$5(rawModel),
14283
14493
  runtime: readOptionalString$5(rawRuntime),
14284
14494
  contextInheritance,
14285
- parentSessionId
14495
+ parentSessionId,
14496
+ metadataOverrides: { session_creation_trigger: structuredClone(trigger) }
14286
14497
  });
14287
14498
  return {
14288
14499
  kind: "nextclaw.session",
@@ -14301,6 +14512,10 @@ var SessionSpawnTool = class {
14301
14512
  if (!sourceSessionId) throw new Error("scope=\"child\" requires an active source session.");
14302
14513
  return sourceSessionId;
14303
14514
  };
14515
+ readTriggerOrThrow = () => {
14516
+ if (!this.trigger) throw new Error("sessions_spawn requires an active run trigger context.");
14517
+ return structuredClone(this.trigger);
14518
+ };
14304
14519
  };
14305
14520
  //#endregion
14306
14521
  //#region src/tools/session-update.tools.ts
@@ -14355,26 +14570,38 @@ var SessionToolProvider = class {
14355
14570
  this.sessionSearch = sessionSearch;
14356
14571
  }
14357
14572
  provide = async (request) => {
14358
- const { toolRunContext } = await this.runContextService.resolve(request);
14573
+ const { session, toolRunContext } = await this.runContextService.resolve(request);
14359
14574
  const { handoffDepth, metadata, sessionId } = toolRunContext;
14360
- const sessionsSpawnTool = new SessionSpawnTool(this.sessionManager, this.sessionRequests);
14361
- sessionsSpawnTool.setContext({
14362
- sourceSessionId: sessionId,
14363
- sourceSessionMetadata: metadata,
14364
- handoffDepth
14575
+ const isChildSession = Boolean(readParentSessionId(session?.metadata ?? metadata));
14576
+ const requestTrigger = createAgentToolRunTriggerInput({
14577
+ request,
14578
+ source: "sessions_request"
14365
14579
  });
14366
14580
  const sessionsRequestTool = new SessionRequestTool(this.sessionRequests);
14367
14581
  sessionsRequestTool.setContext({
14368
14582
  sourceSessionId: sessionId,
14369
- handoffDepth
14583
+ handoffDepth,
14584
+ trigger: requestTrigger
14370
14585
  });
14371
14586
  const tools = [
14372
- sessionsSpawnTool,
14373
14587
  sessionsRequestTool,
14374
14588
  new SessionsListTool(this.sessionManager),
14375
14589
  new SessionsHistoryTool(this.sessionManager),
14376
14590
  new SessionsUpdateTool(this.sessionManager)
14377
14591
  ];
14592
+ if (!isChildSession) {
14593
+ const sessionsSpawnTool = new SessionSpawnTool(this.sessionManager, this.sessionRequests);
14594
+ sessionsSpawnTool.setContext({
14595
+ sourceSessionId: sessionId,
14596
+ sourceSessionMetadata: metadata,
14597
+ handoffDepth,
14598
+ trigger: createAgentToolRunTriggerInput({
14599
+ request,
14600
+ source: "sessions_spawn"
14601
+ })
14602
+ });
14603
+ tools.unshift(sessionsSpawnTool);
14604
+ }
14378
14605
  if (!this.sessionSearch.isReady()) return tools;
14379
14606
  tools.push(new SessionSearchTool({ search: this.sessionSearch.search }, { currentSessionId: sessionId }));
14380
14607
  return tools;
@@ -15242,6 +15469,14 @@ const SESSION_METADATA_LABEL_KEY = "label";
15242
15469
  const CHILD_SESSION_PARENT_METADATA_KEY = "parent_session_id";
15243
15470
  const CHILD_SESSION_REQUEST_METADATA_KEY = "spawned_by_request_id";
15244
15471
  const CHILD_SESSION_LIFECYCLE_METADATA_KEY = "session_lifecycle";
15472
+ async function assertCanCreateSessionFromLineage(parentSessionId, sourceRecord, metadataOverrides, getSessionRecord) {
15473
+ if (Object.prototype.hasOwnProperty.call(metadataOverrides ?? {}, CHILD_SESSION_PARENT_METADATA_KEY)) throw new Error("Session parent must be set through parentSessionId.");
15474
+ if (readOptionalMetadataString(sourceRecord?.metadata?.[CHILD_SESSION_PARENT_METADATA_KEY])) throw new Error(`Child sessions cannot create additional sessions. Source session: ${sourceRecord?.sessionId}.`);
15475
+ if (!parentSessionId) return;
15476
+ const parentRecord = sourceRecord?.sessionId === parentSessionId ? sourceRecord : await getSessionRecord(parentSessionId);
15477
+ if (!parentRecord) throw new Error(`Parent session not found: ${parentSessionId}`);
15478
+ if (readOptionalMetadataString(parentRecord.metadata?.[CHILD_SESSION_PARENT_METADATA_KEY])) throw new Error(`Child sessions cannot create additional sessions. Parent session: ${parentSessionId}.`);
15479
+ }
15245
15480
  function readThinkingEffort(metadata) {
15246
15481
  return readOptionalMetadataString(metadata?.thinkingEffort) ?? readOptionalMetadataString(metadata?.preferred_thinking) ?? readOptionalMetadataString(metadata?.thinking) ?? null;
15247
15482
  }
@@ -17294,6 +17529,7 @@ var SessionManager = class {
17294
17529
  const metadata = cloneInheritedMetadata(sourceSessionMetadata);
17295
17530
  const title = readOptionalString$2(requestedTitle) ?? summarizeTask(task);
17296
17531
  const parentSessionId = readOptionalString$2(rawParentSessionId);
17532
+ await assertCanCreateSessionFromLineage(parentSessionId, sourceRecord, metadataOverrides, this.getSessionRecord);
17297
17533
  const requestId = readOptionalString$2(rawRequestId);
17298
17534
  const sessionType = resolveSessionType({
17299
17535
  runtime,
@@ -17453,23 +17689,9 @@ var SessionManager = class {
17453
17689
  return (liveRecord ? await this.summaryProjection.createWithContextWindow(liveRecord) : await this.getSession(sessionId))?.contextWindow ?? null;
17454
17690
  };
17455
17691
  getAgentRunSession = async (sessionId) => {
17456
- const record = await this.getSessionRecord(sessionId);
17457
- if (!record) throw new Error(`Session not found: ${sessionId}`);
17458
- const agentRuntimeId = readOptionalMetadataString(record.metadata?.agentRuntimeId) ?? "native";
17459
- const model = readOptionalMetadataString(record.metadata?.model);
17460
- return {
17461
- sessionId: record.sessionId,
17462
- agentId: record.agentId,
17463
- agentRuntimeId,
17464
- metadata: structuredClone(record.metadata ?? {}),
17465
- model,
17466
- projectRoot: readProjectRoot(record.metadata),
17467
- workingDir: this.workingDirResolver.resolve({
17468
- agentId: record.agentId,
17469
- metadata: record.metadata
17470
- }),
17471
- thinkingEffort: readThinkingEffort(record.metadata)
17472
- };
17692
+ const summary = await this.options.journalStore.getSessionSummary(sessionId);
17693
+ if (!summary) throw new Error(`Session not found: ${sessionId}`);
17694
+ return this.toAgentRunSession(summary);
17473
17695
  };
17474
17696
  createAgentRunSession = async (params) => {
17475
17697
  const { agentId, agentRuntimeId: requestedAgentRuntimeId, channel, contextInheritance, metadata, model, parentSessionId: rawParentSessionId, peerId: rawPeerId, projectRoot, sessionId, sourceSessionId: rawSourceSessionId, sourceSessionMetadata: requestedSourceSessionMetadata, task, thinkingEffort } = params;
@@ -17520,7 +17742,24 @@ var SessionManager = class {
17520
17742
  };
17521
17743
  getOrCreateAgentRunSession = async (params) => {
17522
17744
  if (!params.sessionId) return await this.createAgentRunSession(params);
17523
- return await this.getSessionRecord(params.sessionId) ? await this.getAgentRunSession(params.sessionId) : await this.createAgentRunSession(params);
17745
+ const summary = await this.options.journalStore.getSessionSummary(params.sessionId);
17746
+ return summary ? this.toAgentRunSession(summary) : await this.createAgentRunSession(params);
17747
+ };
17748
+ toAgentRunSession = (summary) => {
17749
+ const metadata = summary.metadata ?? {};
17750
+ return {
17751
+ sessionId: summary.sessionId,
17752
+ agentId: summary.agentId,
17753
+ agentRuntimeId: readOptionalMetadataString(metadata.agentRuntimeId) ?? "native",
17754
+ metadata: structuredClone(metadata),
17755
+ model: readOptionalMetadataString(metadata.model) ?? readOptionalMetadataString(metadata.preferred_model),
17756
+ projectRoot: readProjectRoot(metadata),
17757
+ workingDir: this.workingDirResolver.resolve({
17758
+ agentId: summary.agentId,
17759
+ metadata
17760
+ }),
17761
+ thinkingEffort: readThinkingEffort(metadata)
17762
+ };
17524
17763
  };
17525
17764
  patchSessionMetadata = async (sessionId, patch) => {
17526
17765
  if (!await this.updateSessionMetadata(sessionId, patch)) throw new Error(`Session metadata was not updated: ${sessionId}`);