@elizaos/plugin-agent-orchestrator 0.3.10 → 0.3.12

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
@@ -98,15 +98,43 @@ var init_ansi_utils = __esm(() => {
98
98
  STATUS_LINE = /^\s*(?:\d+[smh]\s+\d+s?\s*·|↓\s*[\d.]+k?\s*tokens|·\s*↓|esc\s+to\s+interrupt|[Uu]pdate available|ate available|Run:\s+brew|brew\s+upgrade|\d+\s+files?\s+\+\d+\s+-\d+|ctrl\+\w|\+\d+\s+lines|Wrote\s+\d+\s+lines\s+to|\?\s+for\s+shortcuts|Cooked for|Baked for|Cogitated for)/i;
99
99
  });
100
100
 
101
+ // src/services/trajectory-context.ts
102
+ function setTrajectoryContext(runtime, ctx) {
103
+ runtime[CTX_KEY] = ctx;
104
+ }
105
+ function clearTrajectoryContext(runtime) {
106
+ runtime[CTX_KEY] = undefined;
107
+ }
108
+ async function withTrajectoryContext(runtime, ctx, fn) {
109
+ setTrajectoryContext(runtime, ctx);
110
+ try {
111
+ return await fn();
112
+ } finally {
113
+ clearTrajectoryContext(runtime);
114
+ }
115
+ }
116
+ var CTX_KEY = "__orchestratorTrajectoryCtx";
117
+
101
118
  // src/services/swarm-coordinator-prompts.ts
102
119
  function buildSiblingSection(siblings) {
103
120
  if (!siblings || siblings.length === 0)
104
121
  return "";
122
+ const lines = siblings.map((s) => {
123
+ let line = ` - [${s.status}] "${s.label}" (${s.agentType}): ${s.originalTask}`;
124
+ if (s.completionSummary) {
125
+ line += `
126
+ Result: ${s.completionSummary}`;
127
+ } else if (s.lastKeyDecision) {
128
+ line += `
129
+ Latest: ${s.lastKeyDecision}`;
130
+ }
131
+ return line;
132
+ });
105
133
  return `
106
134
  Other agents in this swarm:
107
- ` + siblings.map((s) => ` - [${s.status}] "${s.label}" (${s.agentType}): ${s.originalTask}`).join(`
135
+ ` + lines.join(`
108
136
  `) + `
109
- Use this context when the agent asks creative or architectural questions — ` + `your answer should be consistent with what sibling agents are working on.
137
+ Use this context when the agent asks creative or architectural questions — ` + `your answer should be consistent with what sibling agents are doing.
110
138
  `;
111
139
  }
112
140
  function buildSharedDecisionsSection(decisions) {
@@ -168,6 +196,7 @@ ${recentOutput.slice(-3000)}
168
196
  ` + `- If the agent is asking for information that was NOT provided in the original task ` + `(e.g. which repository to use, project requirements, credentials), ESCALATE. ` + `The coordinator does not have this information — the human must provide it.
169
197
  ` + `- When in doubt, escalate — it's better to ask the human than to make a wrong choice.
170
198
  ` + `- If the agent's output reveals a significant decision that sibling agents should know about ` + `(e.g. chose a library, designed an API shape, picked a UI pattern, established a writing style, ` + `narrowed a research scope, made any choice that affects the shared project), ` + `include "keyDecision" with a brief one-line summary. Skip this for routine tool approvals.
199
+ ` + `- Look for explicit "DECISION:" markers in the agent's output — these are the agent deliberately ` + `surfacing design choices. Always capture these as keyDecision.
171
200
 
172
201
  ` + `Respond with ONLY a JSON object:
173
202
  ` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}`;
@@ -209,6 +238,7 @@ ${recentOutput.slice(-3000)}
209
238
  ` + `- If the agent is clearly mid-operation (build output, test runner, git operations), use "ignore".
210
239
  ` + `- On check ${idleCheckNumber} of ${maxIdleChecks} — if unsure, lean toward "respond" with a nudge rather than "complete".
211
240
  ` + `- If the agent's output reveals a significant creative or architectural decision, ` + `include "keyDecision" with a brief one-line summary.
241
+ ` + `- Look for explicit "DECISION:" markers in the agent's output — always capture these as keyDecision.
212
242
 
213
243
  ` + `Respond with ONLY a JSON object:
214
244
  ` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}`;
@@ -258,6 +288,7 @@ ${turnOutput.slice(-3000)}
258
288
  ` + `- When asking agents to verify work, prefer CLI tools (gh, curl, cat, git diff, etc.) over ` + `browser automation. Browser tools may not be available in headless environments and can cause delays.
259
289
  ` + `- Default to "respond" — only use "complete" when you're certain ALL work is done.
260
290
  ` + `- If the agent's output reveals a significant decision that sibling agents should know about ` + `(e.g. chose a library, designed an API shape, picked a UI pattern, established a writing style, ` + `narrowed a research scope, made any choice that affects the shared project), ` + `include "keyDecision" with a brief one-line summary. Skip this for routine tool approvals.
291
+ ` + `- Look for explicit "DECISION:" markers in the agent's output — these are the agent deliberately ` + `surfacing design choices. Always capture these as keyDecision.
261
292
 
262
293
  ` + `Respond with ONLY a JSON object:
263
294
  ` + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}`;
@@ -295,6 +326,7 @@ ${recentOutput.slice(-3000)}
295
326
  ` + `- When in doubt, escalate.
296
327
 
297
328
  ` + `If the agent's output reveals a significant decision that sibling agents should know about, include "keyDecision" with a brief summary.
329
+ ` + `Look for explicit "DECISION:" markers in the agent's output — always capture these as keyDecision.
298
330
 
299
331
  ` + `Include a JSON action block at the end of your response:
300
332
  ` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}
@@ -332,6 +364,7 @@ ${turnOutput.slice(-3000)}
332
364
  ` + `- When asking agents to verify work, prefer CLI tools (gh, curl, cat, etc.) over browser automation.
333
365
  ` + `- Default to "respond" — only "complete" when certain ALL work is done.
334
366
  ` + `- If the agent's output reveals a significant creative or architectural decision, include "keyDecision" with a brief summary.
367
+ ` + `- Look for explicit "DECISION:" markers in the agent's output — always capture these as keyDecision.
335
368
 
336
369
  ` + `Include a JSON action block at the end of your response:
337
370
  ` + "```json\n" + `{"action": "respond|complete|escalate|ignore", "response": "...", "useKeys": false, "keys": [], "reasoning": "...", "keyDecision": "..."}
@@ -429,7 +462,7 @@ async function classifyEventTier(runtime, ctx, log) {
429
462
  }
430
463
  try {
431
464
  const prompt = buildTriagePrompt(ctx);
432
- const result = await runtime.useModel(ModelType2.TEXT_SMALL, { prompt });
465
+ const result = await withTrajectoryContext(runtime, { source: "orchestrator", decisionType: "event-triage" }, () => runtime.useModel(ModelType2.TEXT_SMALL, { prompt }));
433
466
  const tier = parseTriageResponse(result);
434
467
  if (tier) {
435
468
  log(`Triage: LLM → ${tier}`);
@@ -504,7 +537,8 @@ __export(exports_swarm_decision_loop, {
504
537
  handleBlocked: () => handleBlocked,
505
538
  handleAutonomousDecision: () => handleAutonomousDecision,
506
539
  executeDecision: () => executeDecision,
507
- checkAllTasksComplete: () => checkAllTasksComplete
540
+ checkAllTasksComplete: () => checkAllTasksComplete,
541
+ POST_SEND_COOLDOWN_MS: () => POST_SEND_COOLDOWN_MS
508
542
  });
509
543
  import * as path from "node:path";
510
544
  import { ModelType as ModelType3 } from "@elizaos/core";
@@ -544,11 +578,28 @@ function collectSiblings(ctx, currentSessionId) {
544
578
  for (const [sid, task] of ctx.tasks) {
545
579
  if (sid === currentSessionId)
546
580
  continue;
581
+ let lastKeyDecision;
582
+ for (let i = task.decisions.length - 1;i >= 0; i--) {
583
+ const d = task.decisions[i];
584
+ if (d.reasoning && d.decision !== "auto_resolved") {
585
+ lastKeyDecision = d.reasoning;
586
+ break;
587
+ }
588
+ }
589
+ for (let i = ctx.sharedDecisions.length - 1;i >= 0; i--) {
590
+ const sd = ctx.sharedDecisions[i];
591
+ if (sd.agentLabel === task.label) {
592
+ lastKeyDecision = sd.summary;
593
+ break;
594
+ }
595
+ }
547
596
  siblings.push({
548
597
  label: task.label,
549
598
  agentType: task.agentType,
550
599
  originalTask: task.originalTask,
551
- status: task.status
600
+ status: task.status,
601
+ lastKeyDecision,
602
+ completionSummary: task.completionSummary
552
603
  });
553
604
  }
554
605
  return siblings;
@@ -601,6 +652,17 @@ async function drainPendingTurnComplete(ctx, sessionId) {
601
652
  ctx.log(`Draining buffered turn-complete for "${taskCtx.label}"`);
602
653
  await handleTurnComplete(ctx, sessionId, taskCtx, pendingData);
603
654
  }
655
+ async function drainPendingBlocked(ctx, sessionId) {
656
+ if (!ctx.pendingBlocked.has(sessionId))
657
+ return;
658
+ const pendingData = ctx.pendingBlocked.get(sessionId);
659
+ ctx.pendingBlocked.delete(sessionId);
660
+ const taskCtx = ctx.tasks.get(sessionId);
661
+ if (!taskCtx || taskCtx.status !== "active")
662
+ return;
663
+ ctx.log(`Draining buffered blocked event for "${taskCtx.label}"`);
664
+ await handleBlocked(ctx, sessionId, taskCtx, pendingData);
665
+ }
604
666
  function formatDecisionResponse(decision) {
605
667
  if (decision.action !== "respond")
606
668
  return;
@@ -706,9 +768,15 @@ async function fetchRecentOutput(ctx, sessionId, lines = 50) {
706
768
  async function makeCoordinationDecision(ctx, taskCtx, promptText, recentOutput) {
707
769
  const prompt = buildCoordinationPrompt(toContextSummary(taskCtx), promptText, recentOutput, toDecisionHistory(taskCtx), collectSiblings(ctx, taskCtx.sessionId), ctx.sharedDecisions, ctx.getSwarmContext());
708
770
  try {
709
- const result = await ctx.runtime.useModel(ModelType3.TEXT_SMALL, {
710
- prompt
711
- });
771
+ const result = await withTrajectoryContext(ctx.runtime, {
772
+ source: "orchestrator",
773
+ decisionType: "coordination",
774
+ sessionId: taskCtx.sessionId,
775
+ taskLabel: taskCtx.label,
776
+ repo: taskCtx.repo,
777
+ workdir: taskCtx.workdir,
778
+ originalTask: taskCtx.originalTask
779
+ }, () => ctx.runtime.useModel(ModelType3.TEXT_SMALL, { prompt }));
712
780
  return parseCoordinationResponse(result);
713
781
  } catch (err) {
714
782
  ctx.log(`LLM coordination call failed: ${err}`);
@@ -719,7 +787,8 @@ async function executeDecision(ctx, sessionId, decision) {
719
787
  if (!ctx.ptyService)
720
788
  return;
721
789
  switch (decision.action) {
722
- case "respond":
790
+ case "respond": {
791
+ const taskCtx = ctx.tasks.get(sessionId);
723
792
  if (decision.useKeys && decision.keys) {
724
793
  await ctx.ptyService.sendKeysToSession(sessionId, decision.keys);
725
794
  } else if (decision.response !== undefined) {
@@ -729,7 +798,10 @@ async function executeDecision(ctx, sessionId, decision) {
729
798
  commitSharedDecisionIndex(ctx, sessionId, snapshotIndex);
730
799
  }
731
800
  }
801
+ if (taskCtx)
802
+ taskCtx.lastInputSentAt = Date.now();
732
803
  break;
804
+ }
733
805
  case "complete": {
734
806
  const taskCtx = ctx.tasks.get(sessionId);
735
807
  if (taskCtx) {
@@ -826,6 +898,18 @@ async function handleBlocked(ctx, sessionId, taskCtx, data) {
826
898
  }
827
899
  return;
828
900
  }
901
+ const promptFingerprint = promptText.slice(0, 200);
902
+ if (ctx.inFlightDecisions.has(sessionId)) {
903
+ if (ctx.lastBlockedPromptFingerprint.get(sessionId) === promptFingerprint) {
904
+ ctx.log(`Skipping duplicate blocked event for ${taskCtx.label} (decision in-flight, same prompt)`);
905
+ return;
906
+ }
907
+ ctx.log(`New blocked prompt for ${taskCtx.label} while decision in-flight — buffering`);
908
+ ctx.pendingBlocked.set(sessionId, data);
909
+ ctx.lastBlockedPromptFingerprint.set(sessionId, promptFingerprint);
910
+ return;
911
+ }
912
+ ctx.lastBlockedPromptFingerprint.set(sessionId, promptFingerprint);
829
913
  ctx.broadcast({
830
914
  type: "blocked",
831
915
  sessionId,
@@ -879,6 +963,13 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
879
963
  ctx.pendingTurnComplete.set(sessionId, data);
880
964
  return;
881
965
  }
966
+ if (taskCtx.lastInputSentAt) {
967
+ const elapsed = Date.now() - taskCtx.lastInputSentAt;
968
+ if (elapsed < POST_SEND_COOLDOWN_MS) {
969
+ ctx.log(`Suppressing turn-complete for "${taskCtx.label}" — ` + `${Math.round(elapsed / 1000)}s since last input (cooldown ${POST_SEND_COOLDOWN_MS / 1000}s)`);
970
+ return;
971
+ }
972
+ }
882
973
  ctx.inFlightDecisions.add(sessionId);
883
974
  try {
884
975
  ctx.log(`Turn complete for "${taskCtx.label}" — assessing whether task is done`);
@@ -892,9 +983,15 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
892
983
  const decisionFromPipeline = false;
893
984
  const prompt = buildTurnCompletePrompt(toContextSummary(taskCtx), turnOutput, toDecisionHistory(taskCtx), collectSiblings(ctx, sessionId), ctx.sharedDecisions, ctx.getSwarmContext());
894
985
  try {
895
- const result = await ctx.runtime.useModel(ModelType3.TEXT_SMALL, {
896
- prompt
897
- });
986
+ const result = await withTrajectoryContext(ctx.runtime, {
987
+ source: "orchestrator",
988
+ decisionType: "turn-complete",
989
+ sessionId,
990
+ taskLabel: taskCtx.label,
991
+ repo: taskCtx.repo,
992
+ workdir: taskCtx.workdir,
993
+ originalTask: taskCtx.originalTask
994
+ }, () => ctx.runtime.useModel(ModelType3.TEXT_SMALL, { prompt }));
898
995
  decision = parseCoordinationResponse(result);
899
996
  } catch (err) {
900
997
  ctx.log(`Turn-complete LLM call failed: ${err}`);
@@ -938,6 +1035,7 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
938
1035
  } finally {
939
1036
  ctx.inFlightDecisions.delete(sessionId);
940
1037
  await drainPendingTurnComplete(ctx, sessionId);
1038
+ await drainPendingBlocked(ctx, sessionId);
941
1039
  }
942
1040
  }
943
1041
  async function handleAutonomousDecision(ctx, sessionId, taskCtx, promptText, recentOutput, promptType) {
@@ -1041,6 +1139,7 @@ async function handleAutonomousDecision(ctx, sessionId, taskCtx, promptText, rec
1041
1139
  } finally {
1042
1140
  ctx.inFlightDecisions.delete(sessionId);
1043
1141
  await drainPendingTurnComplete(ctx, sessionId);
1142
+ await drainPendingBlocked(ctx, sessionId);
1044
1143
  }
1045
1144
  }
1046
1145
  async function handleConfirmDecision(ctx, sessionId, taskCtx, promptText, recentOutput, promptType) {
@@ -1117,9 +1216,10 @@ async function handleConfirmDecision(ctx, sessionId, taskCtx, promptText, recent
1117
1216
  } finally {
1118
1217
  ctx.inFlightDecisions.delete(sessionId);
1119
1218
  await drainPendingTurnComplete(ctx, sessionId);
1219
+ await drainPendingBlocked(ctx, sessionId);
1120
1220
  }
1121
1221
  }
1122
- var DECISION_CB_TIMEOUT_MS = 30000, MAX_AUTO_RESPONSES = 10;
1222
+ var DECISION_CB_TIMEOUT_MS = 30000, MAX_AUTO_RESPONSES = 10, POST_SEND_COOLDOWN_MS = 15000;
1123
1223
  var init_swarm_decision_loop = __esm(() => {
1124
1224
  init_ansi_utils();
1125
1225
  init_swarm_event_triage();
@@ -2142,13 +2242,13 @@ var sendToAgentAction = {
2142
2242
  import * as os from "node:os";
2143
2243
  import * as path2 from "node:path";
2144
2244
  import {
2145
- logger as logger3
2245
+ logger as logger4
2146
2246
  } from "@elizaos/core";
2147
2247
 
2148
2248
  // src/services/pty-service.ts
2149
- import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
2249
+ import { appendFile, mkdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
2150
2250
  import { dirname, join as join2 } from "node:path";
2151
- import { logger as logger2 } from "@elizaos/core";
2251
+ import { logger as logger3 } from "@elizaos/core";
2152
2252
  import {
2153
2253
  checkAdapters,
2154
2254
  createAdapter,
@@ -2882,9 +2982,11 @@ async function classifyStallOutput(ctx) {
2882
2982
  }
2883
2983
  try {
2884
2984
  log(`Stall detected for ${sessionId}, asking LLM to classify...`);
2885
- const result = await runtime.useModel(ModelType.TEXT_SMALL, {
2886
- prompt: systemPrompt
2887
- });
2985
+ const result = await withTrajectoryContext(runtime, {
2986
+ source: "orchestrator",
2987
+ decisionType: "stall-classification",
2988
+ sessionId
2989
+ }, () => runtime.useModel(ModelType.TEXT_SMALL, { prompt: systemPrompt }));
2888
2990
  const jsonMatch = result.match(/\{[\s\S]*\}/);
2889
2991
  if (!jsonMatch) {
2890
2992
  log(`Stall classification: no JSON in LLM response`);
@@ -2996,9 +3098,15 @@ async function classifyAndDecideForCoordinator(ctx) {
2996
3098
  }
2997
3099
  try {
2998
3100
  log(`Stall detected for coordinator-managed ${sessionId}, combined classify+decide...`);
2999
- const result = await runtime.useModel(ModelType.TEXT_SMALL, {
3000
- prompt: systemPrompt
3001
- });
3101
+ const result = await withTrajectoryContext(runtime, {
3102
+ source: "orchestrator",
3103
+ decisionType: "stall-classify-decide",
3104
+ sessionId,
3105
+ taskLabel: taskContext.label,
3106
+ repo: taskContext.repo,
3107
+ workdir: taskContext.workdir,
3108
+ originalTask: taskContext.originalTask
3109
+ }, () => runtime.useModel(ModelType.TEXT_SMALL, { prompt: systemPrompt }));
3002
3110
  const jsonMatch = result.match(/\{[\s\S]*\}/);
3003
3111
  if (!jsonMatch) {
3004
3112
  log(`Combined classify+decide: no JSON in LLM response`);
@@ -3045,6 +3153,9 @@ async function classifyAndDecideForCoordinator(ctx) {
3045
3153
  }
3046
3154
  }
3047
3155
 
3156
+ // src/services/pty-service.ts
3157
+ init_swarm_decision_loop();
3158
+
3048
3159
  // src/services/swarm-coordinator.ts
3049
3160
  init_ansi_utils();
3050
3161
  init_swarm_decision_loop();
@@ -3188,9 +3299,15 @@ async function handleIdleCheck(ctx, taskCtx, idleMinutes) {
3188
3299
  const prompt = buildIdleCheckPrompt(contextSummary, recentOutput, idleMinutes, taskCtx.idleCheckCount, MAX_IDLE_CHECKS, decisionHistory, siblings, ctx.sharedDecisions, ctx.getSwarmContext());
3189
3300
  let decision = null;
3190
3301
  try {
3191
- const result = await ctx.runtime.useModel(ModelType4.TEXT_SMALL, {
3192
- prompt
3193
- });
3302
+ const result = await withTrajectoryContext(ctx.runtime, {
3303
+ source: "orchestrator",
3304
+ decisionType: "idle-check",
3305
+ sessionId,
3306
+ taskLabel: taskCtx.label,
3307
+ repo: taskCtx.repo,
3308
+ workdir: taskCtx.workdir,
3309
+ originalTask: taskCtx.originalTask
3310
+ }, () => ctx.runtime.useModel(ModelType4.TEXT_SMALL, { prompt }));
3194
3311
  decision = parseCoordinationResponse(result);
3195
3312
  } catch (err) {
3196
3313
  ctx.log(`Idle check LLM call failed: ${err}`);
@@ -3249,6 +3366,8 @@ class SwarmCoordinator {
3249
3366
  pendingDecisions = new Map;
3250
3367
  inFlightDecisions = new Set;
3251
3368
  pendingTurnComplete = new Map;
3369
+ lastBlockedPromptFingerprint = new Map;
3370
+ pendingBlocked = new Map;
3252
3371
  chatCallback = null;
3253
3372
  wsBroadcast = null;
3254
3373
  agentDecisionCb = null;
@@ -3335,6 +3454,8 @@ class SwarmCoordinator {
3335
3454
  this.pendingDecisions.clear();
3336
3455
  this.inFlightDecisions.clear();
3337
3456
  this.pendingTurnComplete.clear();
3457
+ this.lastBlockedPromptFingerprint.clear();
3458
+ this.pendingBlocked.clear();
3338
3459
  this.unregisteredBuffer.clear();
3339
3460
  this.lastSeenOutput.clear();
3340
3461
  this.lastToolNotification.clear();
@@ -3716,6 +3837,65 @@ class SwarmCoordinator {
3716
3837
  }
3717
3838
  }
3718
3839
 
3840
+ // src/services/debug-capture.ts
3841
+ import { logger as logger2 } from "@elizaos/core";
3842
+ var captureManager = null;
3843
+ var initAttempted = false;
3844
+ function isDebugCaptureEnabled() {
3845
+ return process.env.PARALLAX_DEBUG_CAPTURE === "1";
3846
+ }
3847
+ async function ensureCaptureManager() {
3848
+ if (captureManager)
3849
+ return captureManager;
3850
+ if (initAttempted)
3851
+ return null;
3852
+ initAttempted = true;
3853
+ if (!isDebugCaptureEnabled())
3854
+ return null;
3855
+ try {
3856
+ const mod = await import("pty-state-capture");
3857
+ const { PTYStateCaptureManager } = mod;
3858
+ captureManager = new PTYStateCaptureManager({
3859
+ outputRootDir: ".parallax/pty-captures",
3860
+ defaultRows: 80,
3861
+ defaultCols: 220
3862
+ });
3863
+ logger2.info("[debug-capture] PTY state capture enabled — writing to .parallax/pty-captures/");
3864
+ return captureManager;
3865
+ } catch {
3866
+ logger2.debug("[debug-capture] pty-state-capture not available — capture disabled");
3867
+ return null;
3868
+ }
3869
+ }
3870
+ async function captureSessionOpen(sessionId, agentType) {
3871
+ const mgr = await ensureCaptureManager();
3872
+ if (!mgr)
3873
+ return;
3874
+ try {
3875
+ await mgr.openSession(sessionId, { source: agentType });
3876
+ } catch (err) {
3877
+ logger2.debug(`[debug-capture] Failed to open session ${sessionId}: ${err}`);
3878
+ }
3879
+ }
3880
+ async function captureFeed(sessionId, chunk, direction = "stdout") {
3881
+ if (!captureManager)
3882
+ return;
3883
+ try {
3884
+ await captureManager.feed(sessionId, chunk, direction);
3885
+ } catch (err) {
3886
+ logger2.debug(`[debug-capture] Feed error for ${sessionId}: ${err}`);
3887
+ }
3888
+ }
3889
+ async function captureLifecycle(sessionId, event, detail) {
3890
+ if (!captureManager)
3891
+ return;
3892
+ try {
3893
+ await captureManager.lifecycle(sessionId, event, detail);
3894
+ } catch (err) {
3895
+ logger2.debug(`[debug-capture] Lifecycle error for ${sessionId}: ${err}`);
3896
+ }
3897
+ }
3898
+
3719
3899
  // src/services/pty-service.ts
3720
3900
  function getCoordinator(runtime) {
3721
3901
  const ptyService = runtime.getService("PTY_SERVICE");
@@ -3759,16 +3939,16 @@ class PTYService {
3759
3939
  const existing = servicesMap?.get?.("SWARM_COORDINATOR");
3760
3940
  if (existing && existing.length > 0) {
3761
3941
  service.coordinator = existing[0];
3762
- logger2.info("[PTYService] SwarmCoordinator already registered, skipping duplicate start");
3942
+ logger3.info("[PTYService] SwarmCoordinator already registered, skipping duplicate start");
3763
3943
  } else {
3764
3944
  try {
3765
3945
  const coordinator = new SwarmCoordinator(runtime);
3766
3946
  coordinator.start(service);
3767
3947
  service.coordinator = coordinator;
3768
3948
  servicesMap?.set?.("SWARM_COORDINATOR", [coordinator]);
3769
- logger2.info("[PTYService] SwarmCoordinator wired and started");
3949
+ logger3.info("[PTYService] SwarmCoordinator wired and started");
3770
3950
  } catch (err) {
3771
- logger2.error(`[PTYService] Failed to wire SwarmCoordinator: ${err}`);
3951
+ logger3.error(`[PTYService] Failed to wire SwarmCoordinator: ${err}`);
3772
3952
  }
3773
3953
  }
3774
3954
  return service;
@@ -3950,6 +4130,9 @@ class PTYService {
3950
4130
  this.log(`Failed to write Gemini settings: ${err}`);
3951
4131
  }
3952
4132
  }
4133
+ if (resolvedAgentType !== "shell" && workdir !== process.cwd()) {
4134
+ await this.ensureOrchestratorGitignore(workdir);
4135
+ }
3953
4136
  const spawnConfig = buildSpawnConfig(sessionId, {
3954
4137
  ...options,
3955
4138
  agentType: resolvedAgentType,
@@ -3989,6 +4172,21 @@ class PTYService {
3989
4172
  if (this.usingBunWorker) {
3990
4173
  setupOutputBuffer(ctx, session.id);
3991
4174
  }
4175
+ if (isDebugCaptureEnabled()) {
4176
+ captureSessionOpen(session.id, resolvedAgentType).catch(() => {});
4177
+ if (this.usingBunWorker) {
4178
+ this.manager.onSessionData(session.id, (data) => {
4179
+ captureFeed(session.id, data, "stdout");
4180
+ });
4181
+ } else {
4182
+ const ptySession = this.manager.getSession(session.id);
4183
+ if (ptySession) {
4184
+ ptySession.on("output", (data) => {
4185
+ captureFeed(session.id, data, "stdout");
4186
+ });
4187
+ }
4188
+ }
4189
+ }
3992
4190
  if (resolvedInitialTask) {
3993
4191
  setupDeferredTaskDelivery(ctx, session, resolvedInitialTask, resolvedAgentType);
3994
4192
  }
@@ -4016,6 +4214,7 @@ class PTYService {
4016
4214
  async sendToSession(sessionId, input) {
4017
4215
  if (!this.manager)
4018
4216
  throw new Error("PTYService not initialized");
4217
+ captureFeed(sessionId, input, "stdin");
4019
4218
  return sendToSession(this.ioContext(), sessionId, input);
4020
4219
  }
4021
4220
  async sendKeysToSession(sessionId, keys) {
@@ -4026,6 +4225,7 @@ class PTYService {
4026
4225
  async stopSession(sessionId, force = false) {
4027
4226
  if (!this.manager)
4028
4227
  throw new Error("PTYService not initialized");
4228
+ captureLifecycle(sessionId, "session_stopped", force ? "force" : undefined);
4029
4229
  return stopSession(this.ioContext(), sessionId, this.sessionMetadata, this.sessionWorkdirs, (msg) => this.log(msg), force);
4030
4230
  }
4031
4231
  get defaultApprovalPreset() {
@@ -4101,12 +4301,12 @@ class PTYService {
4101
4301
  handleHookEvent(sessionId, event, data) {
4102
4302
  const summary = event === "tool_running" ? `tool=${data.toolName ?? "?"}` : event === "permission_approved" ? `tool=${data.tool ?? "?"}` : JSON.stringify(data);
4103
4303
  if (event === "tool_running" || event === "permission_approved") {
4104
- logger2.debug(`[PTYService] Hook event for ${sessionId}: ${event} ${summary}`);
4304
+ logger3.debug(`[PTYService] Hook event for ${sessionId}: ${event} ${summary}`);
4105
4305
  } else {
4106
4306
  this.log(`Hook event for ${sessionId}: ${event} ${summary}`);
4107
4307
  }
4108
4308
  if (this.manager && this.usingBunWorker) {
4109
- this.manager.notifyHookEvent(sessionId, event).catch((err) => logger2.debug(`[PTYService] Failed to forward hook event to session: ${err}`));
4309
+ this.manager.notifyHookEvent(sessionId, event).catch((err) => logger3.debug(`[PTYService] Failed to forward hook event to session: ${err}`));
4110
4310
  }
4111
4311
  switch (event) {
4112
4312
  case "tool_running":
@@ -4140,6 +4340,13 @@ class PTYService {
4140
4340
  if (meta?.coordinatorManaged && this.coordinator?.getSupervisionLevel() === "autonomous") {
4141
4341
  const taskCtx = this.coordinator.getTaskContext(sessionId);
4142
4342
  if (taskCtx) {
4343
+ if (taskCtx.lastInputSentAt) {
4344
+ const elapsed = Date.now() - taskCtx.lastInputSentAt;
4345
+ if (elapsed < POST_SEND_COOLDOWN_MS) {
4346
+ this.log(`Suppressing stall classification for ${sessionId} — ` + `${Math.round(elapsed / 1000)}s since coordinator sent input`);
4347
+ return null;
4348
+ }
4349
+ }
4143
4350
  return classifyAndDecideForCoordinator({
4144
4351
  sessionId,
4145
4352
  recentOutput,
@@ -4207,6 +4414,56 @@ class PTYService {
4207
4414
  async writeMemoryFile(agentType, workspacePath, content, options) {
4208
4415
  return this.getAdapter(agentType).writeMemoryFile(workspacePath, content, options);
4209
4416
  }
4417
+ static GITIGNORE_MARKER = "# orchestrator-injected (do not commit agent config/memory files)";
4418
+ static gitignoreLocks = new Map;
4419
+ async ensureOrchestratorGitignore(workdir) {
4420
+ const gitignorePath = join2(workdir, ".gitignore");
4421
+ const existing_lock = PTYService.gitignoreLocks.get(gitignorePath);
4422
+ if (existing_lock)
4423
+ await existing_lock;
4424
+ const task = this.doEnsureGitignore(gitignorePath, workdir);
4425
+ PTYService.gitignoreLocks.set(gitignorePath, task);
4426
+ try {
4427
+ await task;
4428
+ } finally {
4429
+ if (PTYService.gitignoreLocks.get(gitignorePath) === task) {
4430
+ PTYService.gitignoreLocks.delete(gitignorePath);
4431
+ }
4432
+ }
4433
+ }
4434
+ async doEnsureGitignore(gitignorePath, workdir) {
4435
+ let existing = "";
4436
+ try {
4437
+ existing = await readFile2(gitignorePath, "utf-8");
4438
+ } catch {}
4439
+ if (existing.includes(PTYService.GITIGNORE_MARKER))
4440
+ return;
4441
+ const entries = [
4442
+ "",
4443
+ PTYService.GITIGNORE_MARKER,
4444
+ "CLAUDE.md",
4445
+ ".claude/",
4446
+ "GEMINI.md",
4447
+ ".gemini/",
4448
+ ".aider*"
4449
+ ];
4450
+ try {
4451
+ if (existing.length === 0) {
4452
+ await writeFile2(gitignorePath, entries.join(`
4453
+ `) + `
4454
+ `, "utf-8");
4455
+ } else {
4456
+ const separator = existing.endsWith(`
4457
+ `) ? "" : `
4458
+ `;
4459
+ await appendFile(gitignorePath, separator + entries.join(`
4460
+ `) + `
4461
+ `, "utf-8");
4462
+ }
4463
+ } catch (err) {
4464
+ this.log(`Failed to update .gitignore in ${workdir}: ${err}`);
4465
+ }
4466
+ }
4210
4467
  onSessionEvent(callback) {
4211
4468
  this.eventCallbacks.push(callback);
4212
4469
  return () => {
@@ -4254,7 +4511,7 @@ class PTYService {
4254
4511
  return this.metricsTracker.getAll();
4255
4512
  }
4256
4513
  log(message) {
4257
- logger2.debug(`[PTYService] ${message}`);
4514
+ logger3.debug(`[PTYService] ${message}`);
4258
4515
  }
4259
4516
  }
4260
4517
 
@@ -4300,7 +4557,7 @@ var spawnAgentAction = {
4300
4557
  validate: async (runtime, _message) => {
4301
4558
  const ptyService = runtime.getService("PTY_SERVICE");
4302
4559
  if (!ptyService) {
4303
- logger3.warn("[SPAWN_CODING_AGENT] PTYService not available");
4560
+ logger4.warn("[SPAWN_CODING_AGENT] PTYService not available");
4304
4561
  return false;
4305
4562
  }
4306
4563
  return true;
@@ -4413,7 +4670,7 @@ var spawnAgentAction = {
4413
4670
  ptyService.onSessionEvent((sessionId, event, data) => {
4414
4671
  if (sessionId !== session.id)
4415
4672
  return;
4416
- logger3.debug(`[Session ${sessionId}] ${event}: ${JSON.stringify(data)}`);
4673
+ logger4.debug(`[Session ${sessionId}] ${event}: ${JSON.stringify(data)}`);
4417
4674
  if (!coordinator) {
4418
4675
  if (event === "blocked" && callback) {
4419
4676
  callback({
@@ -4465,7 +4722,7 @@ var spawnAgentAction = {
4465
4722
  };
4466
4723
  } catch (error) {
4467
4724
  const errorMessage = error instanceof Error ? error.message : String(error);
4468
- logger3.error("[SPAWN_CODING_AGENT] Failed to spawn agent:", errorMessage);
4725
+ logger4.error("[SPAWN_CODING_AGENT] Failed to spawn agent:", errorMessage);
4469
4726
  if (callback) {
4470
4727
  await callback({
4471
4728
  text: `Failed to spawn coding agent: ${errorMessage}`
@@ -4513,9 +4770,170 @@ var spawnAgentAction = {
4513
4770
 
4514
4771
  // src/actions/coding-task-handlers.ts
4515
4772
  import {
4516
- logger as logger5,
4773
+ logger as logger6,
4517
4774
  ModelType as ModelType5
4518
4775
  } from "@elizaos/core";
4776
+ // src/services/trajectory-feedback.ts
4777
+ var QUERY_TIMEOUT_MS = 5000;
4778
+ function withTimeout2(promise, ms) {
4779
+ return Promise.race([
4780
+ promise,
4781
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Trajectory query timed out after ${ms}ms`)), ms))
4782
+ ]);
4783
+ }
4784
+ function getTrajectoryLogger(runtime) {
4785
+ const runtimeAny = runtime;
4786
+ if (typeof runtimeAny.getService === "function") {
4787
+ const svc = runtimeAny.getService("trajectory_logger");
4788
+ if (svc && typeof svc === "object" && hasListMethod(svc)) {
4789
+ return svc;
4790
+ }
4791
+ }
4792
+ if (typeof runtimeAny.getServicesByType === "function") {
4793
+ const services = runtimeAny.getServicesByType("trajectory_logger");
4794
+ if (Array.isArray(services)) {
4795
+ for (const svc of services) {
4796
+ if (svc && typeof svc === "object" && hasListMethod(svc)) {
4797
+ return svc;
4798
+ }
4799
+ }
4800
+ }
4801
+ }
4802
+ return null;
4803
+ }
4804
+ function hasListMethod(obj) {
4805
+ const candidate = obj;
4806
+ return typeof candidate.listTrajectories === "function" && typeof candidate.getTrajectoryDetail === "function";
4807
+ }
4808
+ function extractInsights(response, purpose) {
4809
+ const insights = [];
4810
+ const decisionPattern = /DECISION:\s*(.+?)(?:\n|$)/gi;
4811
+ let match;
4812
+ while ((match = decisionPattern.exec(response)) !== null) {
4813
+ insights.push(match[1].trim());
4814
+ }
4815
+ const keyDecisionPattern = /"keyDecision"\s*:\s*"([^"]+)"/g;
4816
+ while ((match = keyDecisionPattern.exec(response)) !== null) {
4817
+ insights.push(match[1].trim());
4818
+ }
4819
+ if ((purpose === "turn-complete" || purpose === "coordination") && insights.length === 0) {
4820
+ const reasoningPattern = /"reasoning"\s*:\s*"([^"]{20,200})"/;
4821
+ const reasoningMatch = response.match(reasoningPattern);
4822
+ if (reasoningMatch) {
4823
+ insights.push(reasoningMatch[1].trim());
4824
+ }
4825
+ }
4826
+ return insights;
4827
+ }
4828
+ function isRelevant(experience, taskDescription) {
4829
+ if (!taskDescription)
4830
+ return true;
4831
+ const taskWords = new Set(taskDescription.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((w) => w.length > 3));
4832
+ const insightWords = experience.insight.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((w) => w.length > 3);
4833
+ let overlap = 0;
4834
+ for (const word of insightWords) {
4835
+ if (taskWords.has(word))
4836
+ overlap++;
4837
+ if (overlap >= 2)
4838
+ return true;
4839
+ }
4840
+ return false;
4841
+ }
4842
+ async function queryPastExperience(runtime, options = {}) {
4843
+ const {
4844
+ maxTrajectories = 30,
4845
+ maxEntries = 8,
4846
+ lookbackHours = 48,
4847
+ taskDescription,
4848
+ repo
4849
+ } = options;
4850
+ const logger5 = getTrajectoryLogger(runtime);
4851
+ if (!logger5)
4852
+ return [];
4853
+ const startDate = new Date(Date.now() - lookbackHours * 60 * 60 * 1000).toISOString();
4854
+ try {
4855
+ const result = await withTimeout2(logger5.listTrajectories({
4856
+ source: "orchestrator",
4857
+ limit: maxTrajectories,
4858
+ startDate
4859
+ }), QUERY_TIMEOUT_MS);
4860
+ if (!result.trajectories || result.trajectories.length === 0)
4861
+ return [];
4862
+ const experiences = [];
4863
+ const maxScans = Math.min(result.trajectories.length, maxTrajectories);
4864
+ for (let scanIdx = 0;scanIdx < maxScans; scanIdx++) {
4865
+ const summary = result.trajectories[scanIdx];
4866
+ const detail = await withTimeout2(logger5.getTrajectoryDetail(summary.id), QUERY_TIMEOUT_MS).catch(() => null);
4867
+ if (!detail?.steps)
4868
+ continue;
4869
+ const metadata = detail.metadata;
4870
+ const decisionType = metadata?.orchestrator?.decisionType ?? "unknown";
4871
+ const taskLabel = metadata?.orchestrator?.taskLabel ?? "";
4872
+ const trajectoryRepo = metadata?.orchestrator?.repo;
4873
+ if (repo && (!trajectoryRepo || trajectoryRepo !== repo))
4874
+ continue;
4875
+ for (const step of detail.steps) {
4876
+ if (!step.llmCalls)
4877
+ continue;
4878
+ for (const call of step.llmCalls) {
4879
+ if (!call.response)
4880
+ continue;
4881
+ const insights = extractInsights(call.response, call.purpose ?? decisionType);
4882
+ for (const insight of insights) {
4883
+ experiences.push({
4884
+ timestamp: call.timestamp ?? summary.startTime,
4885
+ decisionType: call.purpose ?? decisionType,
4886
+ taskLabel,
4887
+ insight
4888
+ });
4889
+ }
4890
+ }
4891
+ }
4892
+ }
4893
+ let filtered = taskDescription ? experiences.filter((e) => isRelevant(e, taskDescription)) : experiences;
4894
+ if (filtered.length === 0 && experiences.length > 0) {
4895
+ filtered = experiences;
4896
+ }
4897
+ const seen = new Map;
4898
+ for (const exp of filtered) {
4899
+ const key = exp.insight.toLowerCase();
4900
+ const existing = seen.get(key);
4901
+ if (!existing || exp.timestamp > existing.timestamp) {
4902
+ seen.set(key, exp);
4903
+ }
4904
+ }
4905
+ return Array.from(seen.values()).sort((a, b) => b.timestamp - a.timestamp).slice(0, maxEntries);
4906
+ } catch (err) {
4907
+ console.error("[trajectory-feedback] Failed to query past experience:", err);
4908
+ return [];
4909
+ }
4910
+ }
4911
+ function formatPastExperience(experiences) {
4912
+ if (experiences.length === 0)
4913
+ return "";
4914
+ const lines = experiences.map((e) => {
4915
+ const age = formatAge(e.timestamp);
4916
+ const label = e.taskLabel ? ` [${e.taskLabel}]` : "";
4917
+ return `- ${e.insight}${label} (${age})`;
4918
+ });
4919
+ return `# Past Experience
4920
+
4921
+ ` + `The following decisions and insights were captured from recent agent sessions. ` + `Use them to avoid repeating mistakes and to stay consistent with established patterns.
4922
+
4923
+ ` + `${lines.join(`
4924
+ `)}
4925
+ `;
4926
+ }
4927
+ function formatAge(timestamp) {
4928
+ const diffMs = Date.now() - timestamp;
4929
+ const hours = Math.floor(diffMs / (1000 * 60 * 60));
4930
+ if (hours < 1)
4931
+ return "just now";
4932
+ if (hours < 24)
4933
+ return `${hours}h ago`;
4934
+ const days = Math.floor(hours / 24);
4935
+ return `${days}d ago`;
4936
+ }
4519
4937
 
4520
4938
  // src/actions/coding-task-helpers.ts
4521
4939
  import { randomUUID } from "node:crypto";
@@ -4523,7 +4941,7 @@ import * as fs from "node:fs";
4523
4941
  import * as os2 from "node:os";
4524
4942
  import * as path3 from "node:path";
4525
4943
  import {
4526
- logger as logger4
4944
+ logger as logger5
4527
4945
  } from "@elizaos/core";
4528
4946
  function createScratchDir() {
4529
4947
  const baseDir = path3.join(os2.homedir(), ".milady", "workspaces");
@@ -4568,7 +4986,7 @@ ${preview}` : `Agent "${label}" completed the task.`
4568
4986
  });
4569
4987
  }
4570
4988
  ptyService.stopSession(sessionId, true).catch((err) => {
4571
- logger4.warn(`[START_CODING_TASK] Failed to stop session for "${label}" after task complete: ${err}`);
4989
+ logger5.warn(`[START_CODING_TASK] Failed to stop session for "${label}" after task complete: ${err}`);
4572
4990
  });
4573
4991
  }
4574
4992
  if (event === "error" && callback) {
@@ -4581,7 +4999,7 @@ ${preview}` : `Agent "${label}" completed the task.`
4581
4999
  const wsService = runtime.getService("CODING_WORKSPACE_SERVICE");
4582
5000
  if (wsService) {
4583
5001
  wsService.removeScratchDir(scratchDir).catch((err) => {
4584
- logger4.warn(`[START_CODING_TASK] Failed to cleanup scratch dir for "${label}": ${err}`);
5002
+ logger5.warn(`[START_CODING_TASK] Failed to cleanup scratch dir for "${label}": ${err}`);
4585
5003
  });
4586
5004
  }
4587
5005
  }
@@ -4617,6 +5035,26 @@ function stripAgentPrefix(spec) {
4617
5035
  }
4618
5036
  return spec;
4619
5037
  }
5038
+ function buildSwarmMemoryInstructions(agentLabel, agentTask, allSubtasks, agentIndex) {
5039
+ const siblingTasks = allSubtasks.filter((_, i) => i !== agentIndex).map((t, i) => ` ${i + 1}. ${t}`).join(`
5040
+ `);
5041
+ return `# Swarm Coordination
5042
+
5043
+ ` + `You are agent "${agentLabel}" in a multi-agent swarm of ${allSubtasks.length} agents.
5044
+ ` + `Your task: ${agentTask}
5045
+
5046
+ ` + `Other agents are working on:
5047
+ ${siblingTasks}
5048
+
5049
+ ` + `## Coordination Rules
5050
+
5051
+ ` + `- **Follow the Shared Context exactly.** The planning brief above contains ` + `concrete decisions (names, file paths, APIs, conventions). Use them as-is.
5052
+ ` + `- **Surface design decisions.** If you need to make a creative or architectural ` + `choice not covered by the Shared Context (naming something, choosing a library, ` + `designing an interface, picking an approach), state your decision clearly in your ` + `output so the orchestrator can share it with sibling agents. Write it as:
5053
+ ` + ` "DECISION: [brief description of what you decided and why]"
5054
+ ` + `- **Don't contradict sibling work.** If the orchestrator tells you about decisions ` + `other agents have made, align with them.
5055
+ ` + `- **Ask when uncertain.** If your task depends on another agent's output and you ` + `don't have enough context, ask rather than guessing.
5056
+ `;
5057
+ }
4620
5058
  async function generateSwarmContext(runtime, subtasks, userRequest) {
4621
5059
  const taskList = subtasks.map((t, i) => ` ${i + 1}. ${t}`).join(`
4622
5060
  `);
@@ -4627,25 +5065,28 @@ async function generateSwarmContext(runtime, subtasks, userRequest) {
4627
5065
  ` + `Subtasks being assigned:
4628
5066
  ${taskList}
4629
5067
 
4630
- ` + `Generate a concise shared context brief (3-8 bullet points) covering:
5068
+ ` + `Generate a concise shared context brief (3-10 bullet points) covering:
4631
5069
  ` + `- Project intent and overall goal
4632
5070
  ` + `- Key constraints or preferences from the user's request
4633
5071
  ` + `- Conventions all agents should follow (naming, style, patterns, tone)
4634
5072
  ` + `- How subtasks relate to each other (dependencies, shared interfaces, etc.)
4635
5073
  ` + `- Any decisions that should be consistent across all agents
4636
5074
 
4637
- ` + `Only include what's relevant skip categories that don't apply. ` + `Be specific and actionable, not generic. Keep it under 200 words.
5075
+ ` + `CRITICALConcrete Decisions:
5076
+ ` + `If any subtask involves creative choices (naming a feature, choosing an approach, ` + `designing an API, picking a concept), YOU must make those decisions NOW in this brief. ` + `Do NOT leave creative choices to individual agents — they run in parallel and will ` + `each make different choices, causing inconsistency.
5077
+ ` + `For example: if one agent builds a feature and another writes tests for it, ` + `decide the feature name, file paths, function signatures, and key design choices here ` + `so both agents use the same names and structure.
5078
+
5079
+ ` + `Only include what's relevant — skip categories that don't apply. ` + `Be specific and actionable, not generic. Be as detailed as the task requires — ` + `a trivial task needs a few bullets, a complex task deserves a thorough roadmap.
4638
5080
 
4639
5081
  ` + `Output ONLY the bullet points, no preamble.`;
4640
5082
  try {
4641
- const result = await runtime.useModel(ModelType5.TEXT_SMALL, {
5083
+ const result = await withTrajectoryContext(runtime, { source: "orchestrator", decisionType: "swarm-context-generation" }, () => runtime.useModel(ModelType5.TEXT_SMALL, {
4642
5084
  prompt,
4643
- maxTokens: 400,
4644
5085
  temperature: 0.3
4645
- });
5086
+ }));
4646
5087
  return result?.trim() || "";
4647
5088
  } catch (err) {
4648
- logger5.warn(`Swarm context generation failed: ${err}`);
5089
+ logger6.warn(`Swarm context generation failed: ${err}`);
4649
5090
  return "";
4650
5091
  }
4651
5092
  }
@@ -4703,6 +5144,13 @@ async function handleMultiAgent(ctx, agentsParam) {
4703
5144
  const coordinator = getCoordinator(runtime);
4704
5145
  coordinator?.setSwarmContext(swarmContext);
4705
5146
  }
5147
+ const pastExperience = await queryPastExperience(runtime, {
5148
+ taskDescription: userRequest,
5149
+ lookbackHours: 48,
5150
+ maxEntries: 8,
5151
+ repo
5152
+ });
5153
+ const pastExperienceBlock = formatPastExperience(pastExperience);
4706
5154
  const results = [];
4707
5155
  for (const [i, spec] of agentSpecs.entries()) {
4708
5156
  let specAgentType = defaultAgentType;
@@ -4759,12 +5207,16 @@ ${swarmContext}
4759
5207
  --- End Shared Context ---` : specTask;
4760
5208
  const initialTask = specPiRequested ? toPiCommand(taskWithContext) : taskWithContext;
4761
5209
  const displayType = specPiRequested ? "pi" : specAgentType;
5210
+ const swarmMemory = agentSpecs.length > 1 && swarmContext ? buildSwarmMemoryInstructions(specLabel, specTask, cleanSubtasks, i) : undefined;
5211
+ const agentMemory = [memoryContent, swarmMemory, pastExperienceBlock].filter(Boolean).join(`
5212
+
5213
+ `) || undefined;
4762
5214
  const session = await ptyService.spawnSession({
4763
5215
  name: `coding-${Date.now()}-${i}`,
4764
5216
  agentType: specAgentType,
4765
5217
  workdir,
4766
5218
  initialTask,
4767
- memoryContent,
5219
+ memoryContent: agentMemory,
4768
5220
  credentials,
4769
5221
  approvalPreset: approvalPreset ?? ptyService.defaultApprovalPreset,
4770
5222
  customCredentials,
@@ -4806,7 +5258,7 @@ ${swarmContext}
4806
5258
  }
4807
5259
  } catch (error) {
4808
5260
  const errorMessage = error instanceof Error ? error.message : String(error);
4809
- logger5.error(`[START_CODING_TASK] Failed to spawn agent ${i + 1}:`, errorMessage);
5261
+ logger6.error(`[START_CODING_TASK] Failed to spawn agent ${i + 1}:`, errorMessage);
4810
5262
  results.push({
4811
5263
  sessionId: "",
4812
5264
  agentType: specAgentType,
@@ -4838,7 +5290,7 @@ ${swarmContext}
4838
5290
  };
4839
5291
  }
4840
5292
  async function handleSingleAgent(ctx, task) {
4841
- logger5.debug(`[START_CODING_TASK] handleSingleAgent called, agentType=${ctx.defaultAgentType}, task=${task ? "yes" : "none"}, repo=${ctx.repo ?? "none"}`);
5293
+ logger6.debug(`[START_CODING_TASK] handleSingleAgent called, agentType=${ctx.defaultAgentType}, task=${task ? "yes" : "none"}, repo=${ctx.repo ?? "none"}`);
4842
5294
  const {
4843
5295
  runtime,
4844
5296
  ptyService,
@@ -4898,14 +5350,14 @@ async function handleSingleAgent(ctx, task) {
4898
5350
  } else {
4899
5351
  workdir = createScratchDir();
4900
5352
  }
4901
- logger5.debug(`[START_CODING_TASK] Spawning ${agentType} agent, task: ${task ? `"${task.slice(0, 80)}..."` : "(none)"}, workdir: ${workdir}`);
5353
+ logger6.debug(`[START_CODING_TASK] Spawning ${agentType} agent, task: ${task ? `"${task.slice(0, 80)}..."` : "(none)"}, workdir: ${workdir}`);
4902
5354
  try {
4903
5355
  if (agentType !== "shell" && agentType !== "pi") {
4904
5356
  const [preflight] = await ptyService.checkAvailableAgents([
4905
5357
  agentType
4906
5358
  ]);
4907
5359
  if (preflight && !preflight.installed) {
4908
- logger5.warn(`[START_CODING_TASK] ${preflight.adapter} CLI not installed`);
5360
+ logger6.warn(`[START_CODING_TASK] ${preflight.adapter} CLI not installed`);
4909
5361
  if (callback) {
4910
5362
  await callback({
4911
5363
  text: `${preflight.adapter} CLI is not installed.
@@ -4915,19 +5367,29 @@ Docs: ${preflight.docsUrl}`
4915
5367
  }
4916
5368
  return { success: false, error: "AGENT_NOT_INSTALLED" };
4917
5369
  }
4918
- logger5.debug(`[START_CODING_TASK] Preflight OK: ${preflight?.adapter} installed`);
5370
+ logger6.debug(`[START_CODING_TASK] Preflight OK: ${preflight?.adapter} installed`);
4919
5371
  }
4920
5372
  const piRequested = isPiAgentType(rawAgentType);
4921
5373
  const initialTask = piRequested ? toPiCommand(task) : task;
4922
5374
  const displayType = piRequested ? "pi" : agentType;
5375
+ const pastExperience = await queryPastExperience(runtime, {
5376
+ taskDescription: task,
5377
+ lookbackHours: 48,
5378
+ maxEntries: 6,
5379
+ repo
5380
+ });
5381
+ const pastExperienceBlock = formatPastExperience(pastExperience);
5382
+ const agentMemory = [memoryContent, pastExperienceBlock].filter(Boolean).join(`
5383
+
5384
+ `) || undefined;
4923
5385
  const coordinator = getCoordinator(runtime);
4924
- logger5.debug(`[START_CODING_TASK] Calling spawnSession (${agentType}, coordinator=${!!coordinator})`);
5386
+ logger6.debug(`[START_CODING_TASK] Calling spawnSession (${agentType}, coordinator=${!!coordinator})`);
4925
5387
  const session = await ptyService.spawnSession({
4926
5388
  name: `coding-${Date.now()}`,
4927
5389
  agentType,
4928
5390
  workdir,
4929
5391
  initialTask,
4930
- memoryContent,
5392
+ memoryContent: agentMemory,
4931
5393
  credentials,
4932
5394
  approvalPreset: approvalPreset ?? ptyService.defaultApprovalPreset,
4933
5395
  customCredentials,
@@ -4940,7 +5402,7 @@ Docs: ${preflight.docsUrl}`
4940
5402
  label
4941
5403
  }
4942
5404
  });
4943
- logger5.debug(`[START_CODING_TASK] Session spawned: ${session.id} (${session.status})`);
5405
+ logger6.debug(`[START_CODING_TASK] Session spawned: ${session.id} (${session.status})`);
4944
5406
  const isScratchWorkspace = !repo;
4945
5407
  const scratchDir = isScratchWorkspace ? workdir : null;
4946
5408
  registerSessionEvents(ptyService, runtime, session.id, label, scratchDir, callback, !!coordinator);
@@ -4981,7 +5443,7 @@ Session ID: ${session.id}` });
4981
5443
  };
4982
5444
  } catch (error) {
4983
5445
  const errorMessage = error instanceof Error ? error.message : String(error);
4984
- logger5.error("[START_CODING_TASK] Failed to spawn agent:", errorMessage);
5446
+ logger6.error("[START_CODING_TASK] Failed to spawn agent:", errorMessage);
4985
5447
  if (callback) {
4986
5448
  await callback({
4987
5449
  text: `Failed to start coding agent: ${errorMessage}`
@@ -5162,7 +5624,7 @@ var startCodingTaskAction = {
5162
5624
 
5163
5625
  // src/actions/stop-agent.ts
5164
5626
  import {
5165
- logger as logger6
5627
+ logger as logger7
5166
5628
  } from "@elizaos/core";
5167
5629
  var stopAgentAction = {
5168
5630
  name: "STOP_CODING_AGENT",
@@ -5241,7 +5703,7 @@ var stopAgentAction = {
5241
5703
  try {
5242
5704
  await ptyService.stopSession(session2.id);
5243
5705
  } catch (err) {
5244
- logger6.error(`Failed to stop session ${session2.id}: ${err}`);
5706
+ logger7.error(`Failed to stop session ${session2.id}: ${err}`);
5245
5707
  }
5246
5708
  }
5247
5709
  if (state?.codingSession) {
@@ -7006,5 +7468,5 @@ export {
7006
7468
  CodingWorkspaceService
7007
7469
  };
7008
7470
 
7009
- //# debugId=D706621831E2790E64756E2164756E21
7471
+ //# debugId=4383E51DF7A1F4A764756E2164756E21
7010
7472
  //# sourceMappingURL=index.js.map