@nextclaw/kernel 0.5.1-beta.1 → 0.5.2

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
@@ -4,7 +4,7 @@ import { APP_NAME, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, CLEAR_THINKING_TOK
4
4
  import { NcpEventType, sanitizeAssistantReplyTags } from "@nextclaw/ncp";
5
5
  import { LocalAssetStore, buildAssetContentPath, buildNcpUserContent, buildOpenAiFunctionTool, ncpMessageToOpenAiMessages, validateToolArgs } from "@nextclaw/ncp-agent-runtime";
6
6
  import { createHash, createHmac, randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
7
- import { EventBus, Ingress, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode } from "@nextclaw/shared";
7
+ import { CHAT_SESSION_MATERIALIZATION_METADATA_KEY, EventBus, Ingress, eventKeys, ingressKeys, isRuntimeDefaultModelValue, normalizeRuntimeModelSelectionMode } from "@nextclaw/shared";
8
8
  import { catchError, from, lastValueFrom, tap } from "rxjs";
9
9
  import { appendFileSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
10
10
  import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve } from "node:path";
@@ -584,6 +584,20 @@ function readOptionalString$10(value) {
584
584
  if (typeof value !== "string") return;
585
585
  return value.trim() || void 0;
586
586
  }
587
+ function readSessionMaterialization(metadata) {
588
+ const value = metadata[CHAT_SESSION_MATERIALIZATION_METADATA_KEY];
589
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
590
+ const materialization = value;
591
+ if (materialization.kind !== "child") throw new Error("session_materialization.kind must be \"child\".");
592
+ const parentSessionId = readOptionalString$10(materialization.parentSessionId);
593
+ if (!parentSessionId) throw new Error("session_materialization.parentSessionId is required.");
594
+ if (materialization.inheritContext !== true) throw new Error("session_materialization.inheritContext must be true.");
595
+ return {
596
+ kind: "child",
597
+ parentSessionId,
598
+ inheritContext: true
599
+ };
600
+ }
587
601
  function toRunHandle(accepted) {
588
602
  return {
589
603
  sessionId: accepted.sessionId,
@@ -642,18 +656,7 @@ var AgentRunRequestManager = class {
642
656
  }));
643
657
  };
644
658
  send = async (request) => {
645
- const session = await this.sessionManager.getOrCreateAgentRunSession({
646
- sessionId: request.sessionId,
647
- peerId: request.peerId,
648
- agentId: request.agentId,
649
- agentRuntimeId: request.agentRuntimeId,
650
- channel: request.channel,
651
- metadata: request.metadata,
652
- model: request.model,
653
- projectRoot: request.projectRoot,
654
- task: readMessageTask(request.message),
655
- thinkingEffort: request.thinkingEffort
656
- });
659
+ const session = await this.getOrCreateSessionForRequest(request);
657
660
  const sessionRun = this.sessionRunManager.getSessionRun(session.sessionId) ?? await this.sessionRunManager.createSessionRun(session.sessionId);
658
661
  const message = {
659
662
  ...request.message,
@@ -763,6 +766,25 @@ var AgentRunRequestManager = class {
763
766
  correlationId: request.correlationId
764
767
  };
765
768
  };
769
+ getOrCreateSessionForRequest = async (request) => {
770
+ const sessionMaterialization = readSessionMaterialization(request.metadata ?? {});
771
+ if (sessionMaterialization && (request.sessionId || request.peerId)) throw new Error("session_materialization requires a new session request.");
772
+ return await this.sessionManager.getOrCreateAgentRunSession({
773
+ sessionId: request.sessionId,
774
+ peerId: request.peerId,
775
+ agentId: request.agentId,
776
+ agentRuntimeId: request.agentRuntimeId,
777
+ channel: request.channel,
778
+ contextInheritance: sessionMaterialization ? {} : void 0,
779
+ metadata: request.metadata,
780
+ model: request.model,
781
+ parentSessionId: sessionMaterialization?.parentSessionId,
782
+ projectRoot: request.projectRoot,
783
+ sourceSessionId: sessionMaterialization?.parentSessionId,
784
+ task: readMessageTask(request.message),
785
+ thinkingEffort: request.thinkingEffort
786
+ });
787
+ };
766
788
  abort = async (request) => {
767
789
  this.sessionRunManager.getSessionRun(request.sessionId)?.abortRun(request.runId);
768
790
  };
@@ -3433,29 +3455,53 @@ function upsertNcpAgentSessionSummaryEvent(params) {
3433
3455
  async function replayNcpAgentSessionEvents(events) {
3434
3456
  const stateManager = new DefaultNcpAgentConversationStateManager();
3435
3457
  const knownMessageIds = /* @__PURE__ */ new Set();
3458
+ const toolResultsByCallId = /* @__PURE__ */ new Map();
3436
3459
  for (const event of events) {
3437
3460
  if (isJournalOnlyEvent(event)) continue;
3438
- const replayEvent = createReplayEvent(event);
3461
+ const replayEvent = createReplayEvent(event, toolResultsByCallId);
3439
3462
  const bootstrapEvent = createReplayStreamingBootstrapEvent(replayEvent, knownMessageIds);
3440
3463
  if (bootstrapEvent) await stateManager.dispatch(bootstrapEvent);
3441
3464
  rememberReplayMessageId(replayEvent, knownMessageIds);
3442
3465
  await stateManager.dispatch(replayEvent);
3466
+ if (replayEvent.type === NcpEventType.MessageToolCallResult) toolResultsByCallId.set(replayEvent.payload.toolCallId, replayEvent.payload);
3443
3467
  }
3444
3468
  const snapshot = stateManager.getSnapshot();
3445
3469
  return [...snapshot.messages.map((message) => structuredClone(message)), ...snapshot.streamingMessage ? [structuredClone(snapshot.streamingMessage)] : []];
3446
3470
  }
3447
- function createReplayEvent(event) {
3471
+ function createReplayEvent(event, toolResultsByCallId) {
3448
3472
  const replayEvent = structuredClone(event);
3449
3473
  const replayMessage = readMessageFromSummaryEvent(replayEvent);
3450
3474
  const legacyCompactionMessageId = readLegacyContextCompactionMessageId(replayMessage);
3451
3475
  if (replayMessage && legacyCompactionMessageId) replayMessage.id = legacyCompactionMessageId;
3452
3476
  if (replayMessage?.role === "assistant" && (replayMessage.status === "pending" || replayMessage.status === "streaming")) replayMessage.status = "final";
3453
- if (replayEvent.type === "session.snapshot.message" || replayEvent.type === NcpEventType.MessageCompleted) return {
3454
- type: NcpEventType.MessageSent,
3455
- payload: replayEvent.payload
3456
- };
3477
+ if (replayEvent.type === "session.snapshot.message" || replayEvent.type === NcpEventType.MessageCompleted) {
3478
+ replayEvent.payload.message = mergeReplayCompletedToolResults(replayEvent.payload.message, toolResultsByCallId);
3479
+ return {
3480
+ type: NcpEventType.MessageSent,
3481
+ payload: replayEvent.payload
3482
+ };
3483
+ }
3457
3484
  return replayEvent;
3458
3485
  }
3486
+ function mergeReplayCompletedToolResults(message, toolResultsByCallId) {
3487
+ let changed = false;
3488
+ const parts = message.parts.map((part) => {
3489
+ if (part.type !== "tool-invocation" || part.state === "result" || !part.toolCallId) return part;
3490
+ const result = toolResultsByCallId.get(part.toolCallId);
3491
+ if (!result) return part;
3492
+ changed = true;
3493
+ return {
3494
+ ...part,
3495
+ state: "result",
3496
+ result: result.content,
3497
+ resultContentItems: result.contentItems
3498
+ };
3499
+ });
3500
+ return changed ? {
3501
+ ...message,
3502
+ parts
3503
+ } : message;
3504
+ }
3459
3505
  function readLegacyContextCompactionMessageId(message) {
3460
3506
  const checkpoint = isRecord$11(message?.metadata?.checkpoint) ? message.metadata.checkpoint : null;
3461
3507
  const checkpointId = typeof checkpoint?.id === "string" ? checkpoint.id : "";
@@ -3557,6 +3603,144 @@ function readOptionalMetadataString(value) {
3557
3603
  function readEventSessionId$1(event) {
3558
3604
  return "payload" in event && "sessionId" in event.payload ? readOptionalMetadataString(event.payload.sessionId) : void 0;
3559
3605
  }
3606
+ const DEFAULT_SESSION_LIFECYCLE = "persistent";
3607
+ const SESSION_METADATA_LABEL_KEY = "label";
3608
+ const CHILD_SESSION_PARENT_METADATA_KEY = "parent_session_id";
3609
+ const CHILD_SESSION_REQUEST_METADATA_KEY = "spawned_by_request_id";
3610
+ const CHILD_SESSION_LIFECYCLE_METADATA_KEY = "session_lifecycle";
3611
+ function readThinkingEffort(metadata) {
3612
+ return readOptionalMetadataString(metadata?.thinkingEffort) ?? readOptionalMetadataString(metadata?.preferred_thinking) ?? readOptionalMetadataString(metadata?.thinking) ?? null;
3613
+ }
3614
+ function readProjectRoot(metadata) {
3615
+ return readOptionalMetadataString(metadata?.project_root) ?? readOptionalMetadataString(metadata?.projectRoot);
3616
+ }
3617
+ function readAgentRuntimeId(metadata) {
3618
+ return readOptionalMetadataString(metadata?.agentRuntimeId) ?? readOptionalMetadataString(metadata?.runtime) ?? readOptionalMetadataString(metadata?.session_type);
3619
+ }
3620
+ function summarizeTask(task) {
3621
+ const normalized = task.trim().replace(/\s+/g, " ");
3622
+ if (!normalized) return "Session";
3623
+ return normalized.length <= 72 ? normalized : `${normalized.slice(0, 69)}...`;
3624
+ }
3625
+ function cloneInheritedMetadata(sourceMetadata) {
3626
+ const nextMetadata = {};
3627
+ for (const key of [
3628
+ "runtime",
3629
+ "session_type",
3630
+ "preferred_model",
3631
+ "preferred_thinking",
3632
+ "project_root",
3633
+ "requested_skill_refs",
3634
+ "codex_runtime_backend",
3635
+ "reasoningNormalizationMode",
3636
+ "reasoning_normalization_mode"
3637
+ ]) if (Object.prototype.hasOwnProperty.call(sourceMetadata, key)) nextMetadata[key] = structuredClone(sourceMetadata[key]);
3638
+ return nextMetadata;
3639
+ }
3640
+ function mergeMetadataOverrides(metadata, overrides) {
3641
+ return overrides && Object.keys(overrides).length > 0 ? {
3642
+ ...metadata,
3643
+ ...structuredClone(overrides)
3644
+ } : metadata;
3645
+ }
3646
+ function resolveSessionType(params) {
3647
+ const { metadata, runtime, sessionType } = params;
3648
+ return readOptionalString$8(runtime) ?? readOptionalString$8(metadata.runtime) ?? readOptionalString$8(sessionType) ?? readOptionalString$8(metadata.session_type) ?? "native";
3649
+ }
3650
+ function applySessionOverrides(params) {
3651
+ const { lifecycle, metadata, model, parentSessionId, projectRoot, requestId, sessionType, thinkingLevel, title } = params;
3652
+ metadata.session_type = sessionType;
3653
+ metadata.runtime = sessionType;
3654
+ metadata[SESSION_METADATA_LABEL_KEY] = title;
3655
+ metadata[CHILD_SESSION_LIFECYCLE_METADATA_KEY] = lifecycle;
3656
+ if (parentSessionId) metadata[CHILD_SESSION_PARENT_METADATA_KEY] = parentSessionId;
3657
+ if (requestId) metadata[CHILD_SESSION_REQUEST_METADATA_KEY] = requestId;
3658
+ if (readOptionalString$8(model)) {
3659
+ metadata.model = model?.trim();
3660
+ metadata.preferred_model = model?.trim();
3661
+ }
3662
+ if (readOptionalString$8(thinkingLevel)) {
3663
+ metadata.thinking = thinkingLevel?.trim();
3664
+ metadata.preferred_thinking = thinkingLevel?.trim();
3665
+ }
3666
+ if (readOptionalString$8(projectRoot)) metadata.project_root = projectRoot?.trim();
3667
+ }
3668
+ //#endregion
3669
+ //#region src/utils/session-context-inheritance.utils.ts
3670
+ const CONTEXT_INHERITANCE_METADATA_KEY = "context_inheritance";
3671
+ const INHERITED_FROM_SESSION_METADATA_KEY = "inherited_from_session_id";
3672
+ const INHERITED_FROM_MESSAGE_METADATA_KEY = "inherited_from_message_id";
3673
+ function hasToolCall(message, toolCallId) {
3674
+ return message.parts.some((part) => part.type === "tool-invocation" && part.toolCallId === toolCallId);
3675
+ }
3676
+ function findContextInheritanceAnchor(params) {
3677
+ const anchorToolCallId = readOptionalString$8(params.anchorToolCallId);
3678
+ if (!anchorToolCallId) return null;
3679
+ const index = params.messages.findIndex((message) => hasToolCall(message, anchorToolCallId));
3680
+ const message = index >= 0 ? params.messages[index] : void 0;
3681
+ return message ? {
3682
+ index,
3683
+ message
3684
+ } : null;
3685
+ }
3686
+ function createInheritedContextMessage(params) {
3687
+ const { childSessionId, index, sourceMessage, sourceSessionId } = params;
3688
+ const message = structuredClone(sourceMessage);
3689
+ const metadata = structuredClone(message.metadata ?? {});
3690
+ metadata[INHERITED_FROM_SESSION_METADATA_KEY] = sourceSessionId;
3691
+ metadata[INHERITED_FROM_MESSAGE_METADATA_KEY] = sourceMessage.id;
3692
+ return {
3693
+ ...message,
3694
+ id: `${childSessionId}:inherited:${index + 1}`,
3695
+ sessionId: childSessionId,
3696
+ metadata
3697
+ };
3698
+ }
3699
+ function createInheritedContextSnapshot(params) {
3700
+ const { anchorToolCallId, childSessionId, sourceRecord } = params;
3701
+ const anchor = findContextInheritanceAnchor({
3702
+ anchorToolCallId,
3703
+ messages: sourceRecord.messages
3704
+ });
3705
+ const messages = (anchor ? sourceRecord.messages.slice(0, anchor.index) : sourceRecord.messages).filter((message) => message.status === "final").map((sourceMessage, index) => createInheritedContextMessage({
3706
+ childSessionId,
3707
+ index,
3708
+ sourceMessage,
3709
+ sourceSessionId: sourceRecord.sessionId
3710
+ }));
3711
+ return {
3712
+ messages,
3713
+ metadata: {
3714
+ enabled: true,
3715
+ sourceSessionId: sourceRecord.sessionId,
3716
+ anchorKind: anchor ? "tool_call" : "latest_persisted",
3717
+ anchorToolCallId: readOptionalString$8(anchorToolCallId),
3718
+ anchorMessageId: anchor?.message.id,
3719
+ inheritedMessageCount: messages.length
3720
+ }
3721
+ };
3722
+ }
3723
+ function createSessionContextInheritance(params) {
3724
+ const { childSessionId, contextInheritance, metadata, parentSessionId, sourceRecord } = params;
3725
+ if (!contextInheritance) return {
3726
+ messages: [],
3727
+ metadata
3728
+ };
3729
+ if (!parentSessionId) throw new Error("contextInheritance requires a child session parentSessionId.");
3730
+ if (!sourceRecord) throw new Error("Cannot inherit context because the source session was not found.");
3731
+ const snapshot = createInheritedContextSnapshot({
3732
+ anchorToolCallId: contextInheritance.anchorToolCallId,
3733
+ childSessionId,
3734
+ sourceRecord
3735
+ });
3736
+ return {
3737
+ messages: snapshot.messages,
3738
+ metadata: {
3739
+ ...metadata,
3740
+ [CONTEXT_INHERITANCE_METADATA_KEY]: snapshot.metadata
3741
+ }
3742
+ };
3743
+ }
3560
3744
  //#endregion
3561
3745
  //#region src/contributions/session-activity-preview/utils/session-activity-preview-ncp-event.utils.ts
3562
3746
  const PREVIEW_TEXT_MAX_LENGTH = 160;
@@ -3836,69 +4020,9 @@ var SessionWorkingDirResolver = class {
3836
4020
  };
3837
4021
  //#endregion
3838
4022
  //#region src/managers/session.manager.ts
3839
- const DEFAULT_SESSION_TYPE = "native";
3840
- const DEFAULT_LIFECYCLE = "persistent";
3841
- const SESSION_METADATA_LABEL_KEY = "label";
3842
- const CHILD_SESSION_PARENT_METADATA_KEY = "parent_session_id";
3843
- const CHILD_SESSION_REQUEST_METADATA_KEY = "spawned_by_request_id";
3844
- const CHILD_SESSION_LIFECYCLE_METADATA_KEY = "session_lifecycle";
3845
- function readThinkingEffort(metadata) {
3846
- return readOptionalMetadataString(metadata?.thinkingEffort) ?? null;
3847
- }
3848
- function readProjectRoot(metadata) {
3849
- return readOptionalMetadataString(metadata?.project_root) ?? readOptionalMetadataString(metadata?.projectRoot);
3850
- }
3851
- function summarizeTask(task) {
3852
- const normalized = task.trim().replace(/\s+/g, " ");
3853
- if (!normalized) return "Session";
3854
- return normalized.length <= 72 ? normalized : `${normalized.slice(0, 69)}...`;
3855
- }
3856
4023
  function buildSessionId() {
3857
4024
  return `ncp-${Date.now().toString(36)}-${randomUUID().replace(/-/g, "").slice(0, 8)}`;
3858
4025
  }
3859
- function cloneInheritedMetadata(sourceMetadata) {
3860
- const nextMetadata = {};
3861
- for (const key of [
3862
- "runtime",
3863
- "session_type",
3864
- "preferred_model",
3865
- "preferred_thinking",
3866
- "project_root",
3867
- "requested_skill_refs",
3868
- "codex_runtime_backend",
3869
- "reasoningNormalizationMode",
3870
- "reasoning_normalization_mode"
3871
- ]) if (Object.prototype.hasOwnProperty.call(sourceMetadata, key)) nextMetadata[key] = structuredClone(sourceMetadata[key]);
3872
- return nextMetadata;
3873
- }
3874
- function mergeMetadataOverrides(metadata, overrides) {
3875
- return overrides && Object.keys(overrides).length > 0 ? {
3876
- ...metadata,
3877
- ...structuredClone(overrides)
3878
- } : metadata;
3879
- }
3880
- function resolveSessionType(params) {
3881
- const { metadata, runtime, sessionType } = params;
3882
- return readOptionalString$8(runtime) ?? readOptionalString$8(metadata.runtime) ?? readOptionalString$8(sessionType) ?? readOptionalString$8(metadata.session_type) ?? DEFAULT_SESSION_TYPE;
3883
- }
3884
- function applySessionOverrides(params) {
3885
- const { lifecycle, metadata, model, parentSessionId, projectRoot, requestId, sessionType, thinkingLevel, title } = params;
3886
- metadata.session_type = sessionType;
3887
- metadata.runtime = sessionType;
3888
- metadata[SESSION_METADATA_LABEL_KEY] = title;
3889
- metadata[CHILD_SESSION_LIFECYCLE_METADATA_KEY] = lifecycle;
3890
- if (parentSessionId) metadata[CHILD_SESSION_PARENT_METADATA_KEY] = parentSessionId;
3891
- if (requestId) metadata[CHILD_SESSION_REQUEST_METADATA_KEY] = requestId;
3892
- if (readOptionalString$8(model)) {
3893
- metadata.model = model?.trim();
3894
- metadata.preferred_model = model?.trim();
3895
- }
3896
- if (readOptionalString$8(thinkingLevel)) {
3897
- metadata.thinking = thinkingLevel?.trim();
3898
- metadata.preferred_thinking = thinkingLevel?.trim();
3899
- }
3900
- if (readOptionalString$8(projectRoot)) metadata.project_root = projectRoot?.trim();
3901
- }
3902
4026
  function isSessionSummaryRefreshEvent(event) {
3903
4027
  switch (event.type) {
3904
4028
  case NcpEventType.MessageSent:
@@ -3940,7 +4064,7 @@ var SessionManager = class {
3940
4064
  this.started = false;
3941
4065
  };
3942
4066
  createSession = async (params) => {
3943
- const { agentId: requestedAgentId, metadataOverrides, model, parentSessionId: rawParentSessionId, projectRoot, requestId: rawRequestId, runtime, sessionId: requestedSessionId, sessionType: requestedSessionType, sourceSessionId, sourceSessionMetadata, task, thinkingLevel, title: requestedTitle } = params;
4067
+ const { agentId: requestedAgentId, contextInheritance, metadataOverrides, model, parentSessionId: rawParentSessionId, projectRoot, requestId: rawRequestId, runtime, sessionId: requestedSessionId, sessionType: requestedSessionType, sourceSessionId, sourceSessionMetadata, task, thinkingLevel, title: requestedTitle } = params;
3944
4068
  const sourceRecord = sourceSessionId ? await this.getSessionRecord(sourceSessionId) : null;
3945
4069
  const metadata = cloneInheritedMetadata(sourceSessionMetadata);
3946
4070
  const title = readOptionalString$8(requestedTitle) ?? summarizeTask(task);
@@ -3952,7 +4076,7 @@ var SessionManager = class {
3952
4076
  metadata
3953
4077
  });
3954
4078
  applySessionOverrides({
3955
- lifecycle: DEFAULT_LIFECYCLE,
4079
+ lifecycle: DEFAULT_SESSION_LIFECYCLE,
3956
4080
  metadata,
3957
4081
  model,
3958
4082
  parentSessionId: parentSessionId ?? void 0,
@@ -3966,13 +4090,20 @@ var SessionManager = class {
3966
4090
  const nextMetadata = mergeMetadataOverrides(metadata, metadataOverrides);
3967
4091
  const agentId = readOptionalString$8(requestedAgentId) ?? readOptionalString$8(sourceRecord?.agentId) ?? BUILTIN_MAIN_AGENT_ID;
3968
4092
  const sessionId = readOptionalString$8(requestedSessionId) ?? buildSessionId();
4093
+ const inheritedContext = createSessionContextInheritance({
4094
+ childSessionId: sessionId,
4095
+ contextInheritance,
4096
+ metadata: nextMetadata,
4097
+ parentSessionId,
4098
+ sourceRecord
4099
+ });
3969
4100
  const record = {
3970
4101
  sessionId,
3971
4102
  ...agentId ? { agentId } : {},
3972
- messages: [],
4103
+ messages: inheritedContext.messages,
3973
4104
  createdAt: now,
3974
4105
  updatedAt: now,
3975
- metadata: nextMetadata
4106
+ metadata: inheritedContext.metadata
3976
4107
  };
3977
4108
  await this.options.journalStore.importSessionSnapshot(record);
3978
4109
  await this.publishSessionChange(sessionId);
@@ -3980,12 +4111,12 @@ var SessionManager = class {
3980
4111
  sessionId,
3981
4112
  agentId,
3982
4113
  sessionType,
3983
- runtimeFamily: sessionType === DEFAULT_SESSION_TYPE ? "native" : "external",
4114
+ runtimeFamily: sessionType === "native" ? "native" : "external",
3984
4115
  ...parentSessionId ? { parentSessionId } : {},
3985
4116
  ...requestId ? { spawnedByRequestId: requestId } : {},
3986
- lifecycle: DEFAULT_LIFECYCLE,
4117
+ lifecycle: DEFAULT_SESSION_LIFECYCLE,
3987
4118
  title,
3988
- metadata: nextMetadata,
4119
+ metadata: inheritedContext.metadata,
3989
4120
  createdAt: now,
3990
4121
  updatedAt: now
3991
4122
  };
@@ -4073,9 +4204,13 @@ var SessionManager = class {
4073
4204
  };
4074
4205
  };
4075
4206
  createAgentRunSession = async (params) => {
4076
- const { agentId, agentRuntimeId: requestedAgentRuntimeId, channel, metadata, model, peerId: rawPeerId, projectRoot, sessionId, task, thinkingEffort } = params;
4077
- const agentRuntimeId = requestedAgentRuntimeId ?? "native";
4207
+ const { agentId, agentRuntimeId: requestedAgentRuntimeId, channel, contextInheritance, metadata, model, parentSessionId: rawParentSessionId, peerId: rawPeerId, projectRoot, sessionId, sourceSessionId: rawSourceSessionId, sourceSessionMetadata: requestedSourceSessionMetadata, task, thinkingEffort } = params;
4078
4208
  const peerId = readOptionalString$8(rawPeerId);
4209
+ const parentSessionId = readOptionalString$8(rawParentSessionId);
4210
+ const sourceSessionId = readOptionalString$8(rawSourceSessionId);
4211
+ const sourceRecord = sourceSessionId ? await this.getSessionRecord(sourceSessionId) : null;
4212
+ const sourceSessionMetadata = requestedSourceSessionMetadata ?? sourceRecord?.metadata ?? {};
4213
+ const agentRuntimeId = requestedAgentRuntimeId ?? readAgentRuntimeId(sourceSessionMetadata) ?? "native";
4079
4214
  const peerIdentity = peerId ? createAgentPeerSessionIdentity({
4080
4215
  agentId,
4081
4216
  channel,
@@ -4084,7 +4219,10 @@ var SessionManager = class {
4084
4219
  }) : void 0;
4085
4220
  const requestedSessionId = readOptionalString$8(sessionId);
4086
4221
  const created = await this.createSession({
4087
- sourceSessionMetadata: {},
4222
+ contextInheritance,
4223
+ parentSessionId: parentSessionId ?? void 0,
4224
+ sourceSessionId: sourceSessionId ?? void 0,
4225
+ sourceSessionMetadata,
4088
4226
  sessionId: requestedSessionId ?? peerIdentity?.sessionId,
4089
4227
  task: task ?? "Session",
4090
4228
  agentId,
@@ -4100,16 +4238,16 @@ var SessionManager = class {
4100
4238
  });
4101
4239
  return {
4102
4240
  sessionId: created.sessionId,
4103
- agentId,
4241
+ agentId: created.agentId,
4104
4242
  agentRuntimeId,
4105
4243
  metadata: structuredClone(created.metadata ?? {}),
4106
- model,
4107
- projectRoot,
4244
+ model: model ?? readOptionalMetadataString(created.metadata?.model) ?? readOptionalMetadataString(created.metadata?.preferred_model),
4245
+ projectRoot: projectRoot ?? readProjectRoot(created.metadata),
4108
4246
  workingDir: this.workingDirResolver.resolve({
4109
- agentId,
4247
+ agentId: created.agentId,
4110
4248
  metadata: created.metadata
4111
4249
  }),
4112
- thinkingEffort: thinkingEffort ?? null
4250
+ thinkingEffort: thinkingEffort ?? readThinkingEffort(created.metadata)
4113
4251
  };
4114
4252
  };
4115
4253
  getOrCreateAgentRunSession = async (params) => {
@@ -7127,7 +7265,7 @@ var SessionRequestManager = class {
7127
7265
  this.options = options;
7128
7266
  }
7129
7267
  spawnSessionAndRequest = async (params) => {
7130
- const { sourceSessionId, sourceToolCallId, updateToolCallResult, sourceSessionMetadata, metadataOverrides, task, title, model, runtime, handoffDepth, sessionType, thinkingLevel, projectRoot, agentId, parentSessionId, notify } = params;
7268
+ const { sourceSessionId, sourceToolCallId, updateToolCallResult, sourceSessionMetadata, metadataOverrides, contextInheritance, task, title, model, runtime, handoffDepth, sessionType, thinkingLevel, projectRoot, agentId, parentSessionId, notify } = params;
7131
7269
  const requestId = randomUUID();
7132
7270
  const createdSession = await this.options.sessionManager.createSession({
7133
7271
  sourceSessionId,
@@ -7136,6 +7274,7 @@ var SessionRequestManager = class {
7136
7274
  title,
7137
7275
  sourceSessionMetadata,
7138
7276
  ...metadataOverrides ? { metadataOverrides } : {},
7277
+ contextInheritance,
7139
7278
  agentId,
7140
7279
  model,
7141
7280
  runtime,
@@ -9510,6 +9649,11 @@ function readSpawnNotify(value) {
9510
9649
  if (notifyMode === "none" || notifyMode === "final_reply") return notifyMode;
9511
9650
  throw new Error("notify must be \"none\" or \"final_reply\".");
9512
9651
  }
9652
+ function readInheritContext(value) {
9653
+ if (typeof value === "undefined") return false;
9654
+ if (typeof value === "boolean") return value;
9655
+ throw new Error("inheritContext must be a boolean.");
9656
+ }
9513
9657
  var SessionSpawnTool = class {
9514
9658
  name = "sessions_spawn";
9515
9659
  description = "Create a new session. Use scope=\"child\" to create a child session of the current flow, and add notify when the new session should start immediately.";
@@ -9545,6 +9689,10 @@ var SessionSpawnTool = class {
9545
9689
  type: "string",
9546
9690
  enum: ["none", "final_reply"],
9547
9691
  description: "Optional. Starts the new session immediately. Use \"final_reply\" to continue this session after the new session reaches its final reply, or \"none\" to let it run independently."
9692
+ },
9693
+ inheritContext: {
9694
+ type: "boolean",
9695
+ description: "Child sessions only. When true, the child starts with parent context inherited up to this tool call."
9548
9696
  }
9549
9697
  },
9550
9698
  required: ["task"],
@@ -9563,11 +9711,14 @@ var SessionSpawnTool = class {
9563
9711
  this.handoffDepth = params.handoffDepth ?? 0;
9564
9712
  };
9565
9713
  execute = async (args, context) => {
9566
- const { agentId: rawAgentId, model: rawModel, notify: rawNotify, runtime: rawRuntime, scope: rawScope, task: rawTask, title: rawTitle } = normalizeToolParams(args);
9714
+ const { agentId: rawAgentId, model: rawModel, notify: rawNotify, runtime: rawRuntime, scope: rawScope, task: rawTask, title: rawTitle, inheritContext: rawInheritContext } = normalizeToolParams(args);
9567
9715
  const task = readRequiredString$1(rawTask, "task");
9568
9716
  const scope = readSpawnScope(rawScope);
9569
9717
  const notify = readSpawnNotify(rawNotify);
9718
+ const inheritContext = readInheritContext(rawInheritContext);
9719
+ if (inheritContext && scope !== "child") throw new Error("inheritContext=true requires scope=\"child\".");
9570
9720
  const parentSessionId = scope === "child" ? this.readParentSessionIdOrThrow() : void 0;
9721
+ const contextInheritance = inheritContext ? { anchorToolCallId: context?.toolCallId } : void 0;
9571
9722
  if (notify) return this.sessionRequestManager.spawnSessionAndRequest({
9572
9723
  sourceSessionId: this.sourceSessionId,
9573
9724
  sourceToolCallId: context?.toolCallId,
@@ -9578,6 +9729,7 @@ var SessionSpawnTool = class {
9578
9729
  agentId: readOptionalString$2(rawAgentId),
9579
9730
  model: readOptionalString$2(rawModel),
9580
9731
  runtime: readOptionalString$2(rawRuntime),
9732
+ contextInheritance,
9581
9733
  handoffDepth: this.handoffDepth,
9582
9734
  parentSessionId,
9583
9735
  notify
@@ -9590,6 +9742,7 @@ var SessionSpawnTool = class {
9590
9742
  agentId: readOptionalString$2(rawAgentId),
9591
9743
  model: readOptionalString$2(rawModel),
9592
9744
  runtime: readOptionalString$2(rawRuntime),
9745
+ contextInheritance,
9593
9746
  parentSessionId
9594
9747
  });
9595
9748
  return {