@leanandmean/coding-agent 0.74.1-scramjet.42 → 0.74.1-scramjet.43

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.
@@ -129,6 +129,9 @@ export class AgentSession {
129
129
  // can apply per-section cache control.
130
130
  _baseSystemPromptSections = [];
131
131
  _baseSystemPromptOptions;
132
+ // SCRAMJET-DIVERGENCE: active-tool prompt rebuilds preserve the current top-level run's
133
+ // before_agent_start composition while new prompts and reloads establish a fresh composition (#524).
134
+ _runPromptComposition = { kind: "generated-base" };
132
135
  constructor(config) {
133
136
  this.agent = config.agent;
134
137
  this.sessionManager = config.sessionManager;
@@ -185,8 +188,8 @@ export class AgentSession {
185
188
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
186
189
  }
187
190
  // SCRAMJET-DIVERGENCE: _drainAgentEventQueue ensures all queued async extension event
188
- // processing has settled before proceeding. Used by beforeToolBatch (pre-extraction drain)
189
- // and beforeToolCall (per-tool drain).
191
+ // processing has settled before proceeding. Used by beforeToolBatch (pre-extraction drain),
192
+ // beforeToolCall (per-tool drain), and the awaited turn_end listener barrier.
190
193
  async _drainAgentEventQueue() {
191
194
  await this._agentEventQueue;
192
195
  }
@@ -279,6 +282,10 @@ export class AgentSession {
279
282
  this._agentEventQueue = this._agentEventQueue.then(() => this._processAgentEventTracked(event), () => this._processAgentEventTracked(event));
280
283
  // Keep queue alive if an event handler fails
281
284
  this._agentEventQueue.catch(() => { });
285
+ // SCRAMJET-DIVERGENCE: turn_end tool/prompt changes must settle before Agent snapshots
286
+ // live state for the next provider request (#524).
287
+ if (event.type === "turn_end")
288
+ return this._agentEventQueue;
282
289
  };
283
290
  // SCRAMJET-DIVERGENCE: Capture provider events synchronously and reset stale throughput lifecycles (#476).
284
291
  _captureOutputThroughput(event) {
@@ -952,6 +959,19 @@ export class AgentSession {
952
959
  }
953
960
  return Array.from(unique);
954
961
  }
962
+ _applyRunPromptComposition() {
963
+ switch (this._runPromptComposition.kind) {
964
+ case "generated-base":
965
+ this.agent.state.systemPrompt = this._baseSystemPromptSections.slice();
966
+ break;
967
+ case "contributed-sections":
968
+ this.agent.state.systemPrompt = spliceContributedSections(this._baseSystemPromptSections, this._runPromptComposition.sections);
969
+ break;
970
+ case "authoritative-string":
971
+ this.agent.state.systemPrompt = this._runPromptComposition.systemPrompt;
972
+ break;
973
+ }
974
+ }
955
975
  /** Rebuild `_baseSystemPromptSections` from the current resources and tool set and apply it to agent state. */
956
976
  _rebuildSystemPrompt(toolNames) {
957
977
  const validToolNames = toolNames.filter((name) => this._toolRegistry.has(name));
@@ -985,7 +1005,7 @@ export class AgentSession {
985
1005
  sessionFile: this.sessionManager.getSessionFile(),
986
1006
  };
987
1007
  this._baseSystemPromptSections = buildSystemPromptSections(this._baseSystemPromptOptions);
988
- this.agent.state.systemPrompt = this._baseSystemPromptSections.slice();
1008
+ this._applyRunPromptComposition();
989
1009
  }
990
1010
  // =========================================================================
991
1011
  // Prompting
@@ -1095,6 +1115,7 @@ export class AgentSession {
1095
1115
  messages.push(msg);
1096
1116
  }
1097
1117
  this._pendingNextTurnMessages = [];
1118
+ this._runPromptComposition = { kind: "generated-base" };
1098
1119
  // Emit before_agent_start extension event
1099
1120
  const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPromptSections, this._baseSystemPromptOptions);
1100
1121
  // Add all custom messages from extensions
@@ -1114,15 +1135,18 @@ export class AgentSession {
1114
1135
  // replacement is authoritative (even an empty string): it wins for the
1115
1136
  // whole prompt and any contributed sections are dropped for this turn.
1116
1137
  if (result?.systemPrompt !== undefined) {
1117
- this.agent.state.systemPrompt = result.systemPrompt;
1138
+ this._runPromptComposition = {
1139
+ kind: "authoritative-string",
1140
+ systemPrompt: result.systemPrompt,
1141
+ };
1118
1142
  }
1119
1143
  else if (result?.systemPromptSections) {
1120
- this.agent.state.systemPrompt = spliceContributedSections(this._baseSystemPromptSections, result.systemPromptSections);
1121
- }
1122
- else {
1123
- // Ensure we're using the base prompt (in case previous turn had modifications)
1124
- this.agent.state.systemPrompt = this._baseSystemPromptSections.slice();
1144
+ this._runPromptComposition = {
1145
+ kind: "contributed-sections",
1146
+ sections: result.systemPromptSections.map((section) => ({ ...section })),
1147
+ };
1125
1148
  }
1149
+ this._applyRunPromptComposition();
1126
1150
  }
1127
1151
  catch (error) {
1128
1152
  preflightResult?.(false);
@@ -2268,6 +2292,7 @@ export class AgentSession {
2268
2292
  await this._resourceLoader.reload();
2269
2293
  await emitSessionShutdownEvent(this._extensionRunner, { type: "session_shutdown", reason: "reload" });
2270
2294
  resetApiProviders();
2295
+ this._runPromptComposition = { kind: "generated-base" };
2271
2296
  this._buildRuntime({
2272
2297
  activeToolNames: this.getActiveToolNames(),
2273
2298
  flagValues: previousFlagValues,