open-agents-ai 0.62.1 → 0.64.0
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/README.md +16 -4
- package/dist/index.js +199 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -519,7 +519,7 @@ The agent stack includes a real-time emotion system that modulates behavior base
|
|
|
519
519
|
- **Valence** (-1 to +1): displeasure ↔ pleasure
|
|
520
520
|
- **Arousal** (0 to 1): calm ↔ energized
|
|
521
521
|
|
|
522
|
-
Every agent event (tool success/failure, task completion, errors, context pressure) is appraised and shifts the emotional state, which decays back toward a baseline over ~
|
|
522
|
+
Every agent event (tool success/failure, task completion, errors, context pressure) is appraised and shifts the emotional state, which decays back toward a baseline over ~5 minutes. The emotional state modulates agent behavior across all layers: system prompt behavioral hints, voice narration tone, and decision-making style:
|
|
523
523
|
|
|
524
524
|
| Quadrant | Valence | Arousal | Behavioral Effect |
|
|
525
525
|
|----------|---------|---------|-------------------|
|
|
@@ -854,19 +854,31 @@ The TUI features an animated multilingual phrase carousel, live metrics bar with
|
|
|
854
854
|
|
|
855
855
|
All settings commands accept `--local` to save to project `.oa/settings.json` instead of global config.
|
|
856
856
|
|
|
857
|
-
### Mid-Task Steering
|
|
857
|
+
### Mid-Task Steering (Sub-Agent Architecture)
|
|
858
858
|
|
|
859
|
-
While the agent is working (shown by the `+` prompt), type to add context:
|
|
859
|
+
While the agent is working (shown by the `+` prompt), type to add context. A **dedicated steering sub-agent** spins up in the background to process your input:
|
|
860
|
+
|
|
861
|
+
1. **Immediate acknowledgment** — the steering agent speaks a brief response via TTS (e.g., "Got it, I'll adjust the approach")
|
|
862
|
+
2. **Context expansion** — your terse input is expanded into a structured steering instruction grounded in the current task goal and recent agent activity
|
|
863
|
+
3. **Non-blocking injection** — the expanded instruction is injected into the main agent's context at the next turn boundary, without interrupting the current tool call
|
|
860
864
|
|
|
861
865
|
```
|
|
862
866
|
> fix the auth bug
|
|
863
867
|
⎿ Read: src/auth.ts
|
|
864
868
|
+ also check the session handling ← typed while agent works
|
|
865
|
-
|
|
869
|
+
🔊 "Got it, adjusting to include session handling"
|
|
870
|
+
↪ USER STEERING: Check session handling in addition to auth...
|
|
866
871
|
⎿ Search: session
|
|
867
872
|
⎿ Edit: src/auth.ts
|
|
868
873
|
```
|
|
869
874
|
|
|
875
|
+
The steering sub-agent uses the same model and backend as the main agent with `maxTurns: 3` and `maxTokens: 512` for fast response. If the steering agent fails, the raw input is injected as a fallback.
|
|
876
|
+
|
|
877
|
+
**Research foundations:**
|
|
878
|
+
- **ReAct** (Yao et al., 2023) — interleaved reasoning + acting benefits from external course corrections grounded in current state
|
|
879
|
+
- **LATS** (Zhou et al., 2024) — mid-execution replanning with user-provided value signals improves task completion on complex multi-step problems
|
|
880
|
+
- **AutoGen** (Wu et al., 2023) — human-in-the-loop patterns work best when user messages are expanded into structured instructions, reducing ambiguity for the primary agent
|
|
881
|
+
|
|
870
882
|
## Tools (47)
|
|
871
883
|
|
|
872
884
|
| Tool | Description |
|
package/dist/index.js
CHANGED
|
@@ -27076,7 +27076,52 @@ function getTier(personality) {
|
|
|
27076
27076
|
return "conv";
|
|
27077
27077
|
return "chatty";
|
|
27078
27078
|
}
|
|
27079
|
-
function
|
|
27079
|
+
function emotionColor(emotion) {
|
|
27080
|
+
if (!emotion)
|
|
27081
|
+
return "";
|
|
27082
|
+
const vDist = Math.abs(emotion.valence - 0.1);
|
|
27083
|
+
const aDist = Math.abs(emotion.arousal - 0.3);
|
|
27084
|
+
if (vDist < 0.15 && aDist < 0.15)
|
|
27085
|
+
return "";
|
|
27086
|
+
if (Math.random() > 0.3)
|
|
27087
|
+
return "";
|
|
27088
|
+
if (emotion.valence > 0.5 && emotion.arousal > 0.6) {
|
|
27089
|
+
return pick("emo_excited", [
|
|
27090
|
+
"Feeling good about this. ",
|
|
27091
|
+
"On a roll here. ",
|
|
27092
|
+
"This is going great. ",
|
|
27093
|
+
"Momentum building. ",
|
|
27094
|
+
"Loving this. "
|
|
27095
|
+
]);
|
|
27096
|
+
}
|
|
27097
|
+
if (emotion.valence < -0.3 && emotion.arousal > 0.5) {
|
|
27098
|
+
return pick("emo_stressed", [
|
|
27099
|
+
"Pushing through. ",
|
|
27100
|
+
"Staying focused. ",
|
|
27101
|
+
"Not giving up. ",
|
|
27102
|
+
"Determined to crack this. ",
|
|
27103
|
+
"Bear with me. "
|
|
27104
|
+
]);
|
|
27105
|
+
}
|
|
27106
|
+
if (emotion.valence > 0.3 && emotion.arousal < 0.3) {
|
|
27107
|
+
return pick("emo_calm", [
|
|
27108
|
+
"Nice and steady. ",
|
|
27109
|
+
"Smooth sailing. ",
|
|
27110
|
+
"Easy does it. ",
|
|
27111
|
+
"Taking it easy. "
|
|
27112
|
+
]);
|
|
27113
|
+
}
|
|
27114
|
+
if (emotion.valence < -0.2 && emotion.arousal < 0.3) {
|
|
27115
|
+
return pick("emo_subdued", [
|
|
27116
|
+
"Being careful here. ",
|
|
27117
|
+
"Treading carefully. ",
|
|
27118
|
+
"Let me be precise. ",
|
|
27119
|
+
"Taking extra care. "
|
|
27120
|
+
]);
|
|
27121
|
+
}
|
|
27122
|
+
return "";
|
|
27123
|
+
}
|
|
27124
|
+
function describeToolCall(toolName, args, personality = 2, emotion) {
|
|
27080
27125
|
const path = args["path"];
|
|
27081
27126
|
const file = path ? path.split("/").pop() ?? path : "";
|
|
27082
27127
|
const tier = getTier(personality);
|
|
@@ -27235,13 +27280,15 @@ function describeToolCall(toolName, args, personality = 2) {
|
|
|
27235
27280
|
break;
|
|
27236
27281
|
}
|
|
27237
27282
|
}
|
|
27283
|
+
const emoPrefix = personality >= 3 ? emotionColor(emotion) : "";
|
|
27238
27284
|
let result;
|
|
27239
|
-
|
|
27285
|
+
const fullPrefix = emoPrefix + prefix;
|
|
27286
|
+
if (!fullPrefix) {
|
|
27240
27287
|
result = base;
|
|
27241
|
-
} else if (
|
|
27242
|
-
result =
|
|
27288
|
+
} else if (fullPrefix.endsWith(". ") || fullPrefix.endsWith("! ")) {
|
|
27289
|
+
result = fullPrefix + base;
|
|
27243
27290
|
} else if (base.length > 0) {
|
|
27244
|
-
result =
|
|
27291
|
+
result = fullPrefix + base.charAt(0).toLowerCase() + base.slice(1);
|
|
27245
27292
|
} else {
|
|
27246
27293
|
result = base;
|
|
27247
27294
|
}
|
|
@@ -27305,7 +27352,7 @@ function extractResultDigest(toolName, content) {
|
|
|
27305
27352
|
const digest = nuggets.slice(0, 3).join(", ");
|
|
27306
27353
|
return digest.length > 100 ? digest.slice(0, 97) + "..." : digest;
|
|
27307
27354
|
}
|
|
27308
|
-
function describeToolResult(toolName, success, personality = 2, resultContent) {
|
|
27355
|
+
function describeToolResult(toolName, success, personality = 2, resultContent, emotion) {
|
|
27309
27356
|
if (toolName === "task_complete")
|
|
27310
27357
|
return "";
|
|
27311
27358
|
const tier = getTier(personality);
|
|
@@ -27313,24 +27360,25 @@ function describeToolResult(toolName, success, personality = 2, resultContent) {
|
|
|
27313
27360
|
if (digest) {
|
|
27314
27361
|
narration.lastResultDigest = digest;
|
|
27315
27362
|
}
|
|
27363
|
+
const emo = personality >= 3 ? emotionColor(emotion) : "";
|
|
27316
27364
|
if (success) {
|
|
27317
27365
|
narration.consecutiveErrors = 0;
|
|
27318
|
-
if (!digest && personality >= 3 && Math.random() < 0.4)
|
|
27366
|
+
if (!digest && !emo && personality >= 3 && Math.random() < 0.4)
|
|
27319
27367
|
return "";
|
|
27320
27368
|
const base = pick(`result_ok_${tier}`, RESULT_SUCCESS_VARIANTS[tier] ?? RESULT_SUCCESS_VARIANTS.terse);
|
|
27321
27369
|
if (digest && personality >= 2) {
|
|
27322
27370
|
const connectors = tier === "chatty" ? [` \u2014 ${digest}`, `, I see ${digest}`, `. Looks like ${digest}`, `, showing ${digest}`] : tier === "conv" ? [` \u2014 ${digest}`, `, ${digest}`, `. Shows ${digest}`] : [`: ${digest}`];
|
|
27323
|
-
return base + pick("digest_conn_ok", connectors);
|
|
27371
|
+
return emo + base + pick("digest_conn_ok", connectors);
|
|
27324
27372
|
}
|
|
27325
|
-
return base;
|
|
27373
|
+
return emo + base;
|
|
27326
27374
|
}
|
|
27327
27375
|
narration.consecutiveErrors++;
|
|
27328
27376
|
narration.totalErrors++;
|
|
27329
27377
|
const failBase = narration.consecutiveErrors >= 3 ? pick(`result_multifail_${tier}`, RESULT_MULTI_FAIL_VARIANTS[tier] ?? RESULT_MULTI_FAIL_VARIANTS.terse) : pick(`result_fail_${tier}`, RESULT_FAIL_VARIANTS[tier] ?? RESULT_FAIL_VARIANTS.terse);
|
|
27330
27378
|
if (digest && personality >= 2) {
|
|
27331
|
-
return `${failBase} \u2014 ${digest}`;
|
|
27379
|
+
return emo + `${failBase} \u2014 ${digest}`;
|
|
27332
27380
|
}
|
|
27333
|
-
return failBase;
|
|
27381
|
+
return emo + failBase;
|
|
27334
27382
|
}
|
|
27335
27383
|
function describeTaskComplete(summary, completed, personality = 2) {
|
|
27336
27384
|
const truncated = summary.length > 300 ? summary.slice(0, 300) + "..." : summary;
|
|
@@ -31519,26 +31567,57 @@ Call task_complete with the JSON array when done.`, onEvent)
|
|
|
31519
31567
|
});
|
|
31520
31568
|
|
|
31521
31569
|
// packages/cli/dist/tui/emotion-engine.js
|
|
31570
|
+
function labelFromCoordinates(valence, arousal) {
|
|
31571
|
+
if (valence > 0.6 && arousal > 0.6)
|
|
31572
|
+
return { label: "exhilarated", emoji: "\u{1F929}" };
|
|
31573
|
+
if (valence > 0.4 && arousal > 0.5)
|
|
31574
|
+
return { label: "excited", emoji: "\u{1F525}" };
|
|
31575
|
+
if (valence > 0.25 && arousal > 0.45)
|
|
31576
|
+
return { label: "energized", emoji: "\u26A1" };
|
|
31577
|
+
if (valence < -0.6 && arousal > 0.6)
|
|
31578
|
+
return { label: "distressed", emoji: "\u{1F630}" };
|
|
31579
|
+
if (valence < -0.3 && arousal > 0.5)
|
|
31580
|
+
return { label: "frustrated", emoji: "\u{1F624}" };
|
|
31581
|
+
if (valence < -0.15 && arousal > 0.4)
|
|
31582
|
+
return { label: "tense", emoji: "\u{1F62C}" };
|
|
31583
|
+
if (valence < -0.5 && arousal < 0.25)
|
|
31584
|
+
return { label: "dejected", emoji: "\u{1F61E}" };
|
|
31585
|
+
if (valence < -0.3 && arousal < 0.35)
|
|
31586
|
+
return { label: "weary", emoji: "\u{1F62E}\u200D\u{1F4A8}" };
|
|
31587
|
+
if (valence < -0.1 && arousal < 0.3)
|
|
31588
|
+
return { label: "cautious", emoji: "\u{1F610}" };
|
|
31589
|
+
if (valence > 0.5 && arousal < 0.25)
|
|
31590
|
+
return { label: "serene", emoji: "\u{1F60C}" };
|
|
31591
|
+
if (valence > 0.3 && arousal < 0.35)
|
|
31592
|
+
return { label: "content", emoji: "\u{1F60A}" };
|
|
31593
|
+
if (valence > 0.15 && arousal < 0.35)
|
|
31594
|
+
return { label: "calm", emoji: "\u{1F642}" };
|
|
31595
|
+
if (valence > 0.2)
|
|
31596
|
+
return { label: "optimistic", emoji: "\u{1F642}" };
|
|
31597
|
+
if (valence < -0.1)
|
|
31598
|
+
return { label: "wary", emoji: "\u{1F928}" };
|
|
31599
|
+
return { label: "curious", emoji: "\u{1F914}" };
|
|
31600
|
+
}
|
|
31522
31601
|
function appraiseEvent(event) {
|
|
31523
31602
|
switch (event.type) {
|
|
31524
31603
|
case "tool_result":
|
|
31525
31604
|
if (event.success) {
|
|
31526
|
-
return { valence: 0.
|
|
31605
|
+
return { valence: 0.12, arousal: 0.06 };
|
|
31527
31606
|
} else {
|
|
31528
|
-
return { valence: -0.
|
|
31607
|
+
return { valence: -0.2, arousal: 0.15 };
|
|
31529
31608
|
}
|
|
31530
31609
|
case "complete":
|
|
31531
|
-
return { valence: 0.
|
|
31610
|
+
return { valence: 0.4, arousal: 0.3 };
|
|
31532
31611
|
case "error":
|
|
31533
|
-
return { valence: -0.
|
|
31612
|
+
return { valence: -0.35, arousal: 0.25 };
|
|
31534
31613
|
case "compaction":
|
|
31535
|
-
return { valence: -0.
|
|
31614
|
+
return { valence: -0.1, arousal: 0.12 };
|
|
31536
31615
|
case "tool_call":
|
|
31537
|
-
return { valence: 0.
|
|
31616
|
+
return { valence: 0.03, arousal: 0.05 };
|
|
31538
31617
|
case "stream_start":
|
|
31539
|
-
return { valence: 0.
|
|
31618
|
+
return { valence: 0.05, arousal: 0.06 };
|
|
31540
31619
|
case "user_interrupt":
|
|
31541
|
-
return { valence: -0.
|
|
31620
|
+
return { valence: -0.1, arousal: 0.1 };
|
|
31542
31621
|
default:
|
|
31543
31622
|
return null;
|
|
31544
31623
|
}
|
|
@@ -31553,12 +31632,12 @@ var init_emotion_engine = __esm({
|
|
|
31553
31632
|
init_dist5();
|
|
31554
31633
|
BASELINE_VALENCE = 0.1;
|
|
31555
31634
|
BASELINE_AROUSAL = 0.3;
|
|
31556
|
-
DECAY_HALF_LIFE_MS =
|
|
31557
|
-
LABEL_UPDATE_INTERVAL_MS =
|
|
31635
|
+
DECAY_HALF_LIFE_MS = 3e5;
|
|
31636
|
+
LABEL_UPDATE_INTERVAL_MS = 15e3;
|
|
31558
31637
|
EXCITEMENT_THRESHOLD = 0.85;
|
|
31559
31638
|
DISTRESS_THRESHOLD = -0.7;
|
|
31560
31639
|
OUTREACH_COOLDOWN_MS = 3e5;
|
|
31561
|
-
LABEL_REGEN_THRESHOLD = 0.
|
|
31640
|
+
LABEL_REGEN_THRESHOLD = 0.06;
|
|
31562
31641
|
EmotionEngine = class {
|
|
31563
31642
|
state = {
|
|
31564
31643
|
valence: BASELINE_VALENCE,
|
|
@@ -31572,6 +31651,9 @@ var init_emotion_engine = __esm({
|
|
|
31572
31651
|
lastLabelUpdate = 0;
|
|
31573
31652
|
lastOutreach = 0;
|
|
31574
31653
|
labelUpdatePending = false;
|
|
31654
|
+
/** Valence/arousal snapshot at last label regen — for change detection */
|
|
31655
|
+
lastLabelValence = BASELINE_VALENCE;
|
|
31656
|
+
lastLabelArousal = BASELINE_AROUSAL;
|
|
31575
31657
|
/** Running counters for context-aware appraisal */
|
|
31576
31658
|
consecutiveFailures = 0;
|
|
31577
31659
|
consecutiveSuccesses = 0;
|
|
@@ -31623,22 +31705,24 @@ ${behavioralHint}`;
|
|
|
31623
31705
|
}
|
|
31624
31706
|
}
|
|
31625
31707
|
let momentum = 1;
|
|
31626
|
-
if (this.consecutiveSuccesses >=
|
|
31627
|
-
momentum = 1 + (this.consecutiveSuccesses -
|
|
31708
|
+
if (this.consecutiveSuccesses >= 2) {
|
|
31709
|
+
momentum = 1 + (this.consecutiveSuccesses - 1) * 0.2;
|
|
31628
31710
|
}
|
|
31629
31711
|
if (this.consecutiveFailures >= 2) {
|
|
31630
|
-
momentum = 1 + (this.consecutiveFailures - 1) * 0.
|
|
31712
|
+
momentum = 1 + (this.consecutiveFailures - 1) * 0.25;
|
|
31631
31713
|
}
|
|
31632
|
-
const prevValence = this.state.valence;
|
|
31633
|
-
const prevArousal = this.state.arousal;
|
|
31634
31714
|
this.state.valence = clamp(this.state.valence + delta.valence * momentum, -1, 1);
|
|
31635
31715
|
this.state.arousal = clamp(this.state.arousal + delta.arousal * momentum, 0, 1);
|
|
31636
31716
|
this.state.updatedAt = Date.now();
|
|
31637
|
-
const
|
|
31638
|
-
|
|
31639
|
-
|
|
31717
|
+
const deterministicLabel = labelFromCoordinates(this.state.valence, this.state.arousal);
|
|
31718
|
+
this.state.label = deterministicLabel.label;
|
|
31719
|
+
this.state.emoji = deterministicLabel.emoji;
|
|
31720
|
+
const valenceShift = Math.abs(this.state.valence - this.lastLabelValence);
|
|
31721
|
+
const arousalShift = Math.abs(this.state.arousal - this.lastLabelArousal);
|
|
31722
|
+
const significantDrift = valenceShift > LABEL_REGEN_THRESHOLD || arousalShift > LABEL_REGEN_THRESHOLD;
|
|
31640
31723
|
const now = Date.now();
|
|
31641
|
-
|
|
31724
|
+
const cooldownElapsed = now - this.lastLabelUpdate > LABEL_UPDATE_INTERVAL_MS;
|
|
31725
|
+
if (cooldownElapsed && !this.labelUpdatePending && significantDrift) {
|
|
31642
31726
|
this.regenerateLabel();
|
|
31643
31727
|
}
|
|
31644
31728
|
this.config.onEmotionUpdate?.(this.getState());
|
|
@@ -31732,6 +31816,8 @@ Example: \u{1F30A} flowing`;
|
|
|
31732
31816
|
this.state.label = wordMatch[0].toLowerCase();
|
|
31733
31817
|
}
|
|
31734
31818
|
this.lastLabelUpdate = Date.now();
|
|
31819
|
+
this.lastLabelValence = this.state.valence;
|
|
31820
|
+
this.lastLabelArousal = this.state.arousal;
|
|
31735
31821
|
this.config.onEmotionUpdate?.(this.getState());
|
|
31736
31822
|
} catch {
|
|
31737
31823
|
} finally {
|
|
@@ -34581,7 +34667,9 @@ ${entry.fullContent}`
|
|
|
34581
34667
|
statusBar?.setActiveTool(event.toolName ?? null);
|
|
34582
34668
|
contentWrite(() => {
|
|
34583
34669
|
if (voice?.enabled) {
|
|
34584
|
-
const
|
|
34670
|
+
const emoState = emotionEngine?.getState();
|
|
34671
|
+
const emoCtx = emoState ? { valence: emoState.valence, arousal: emoState.arousal, label: emoState.label, emoji: emoState.emoji } : void 0;
|
|
34672
|
+
const desc = describeToolCall(event.toolName ?? "unknown", event.toolArgs ?? {}, vLevel, emoCtx);
|
|
34585
34673
|
renderVoiceText(desc);
|
|
34586
34674
|
voice.speak(desc);
|
|
34587
34675
|
}
|
|
@@ -34616,7 +34704,9 @@ ${entry.fullContent}`
|
|
|
34616
34704
|
renderVerbose(`${event.toolName ?? "unknown"}: ${durStr}${sizeStr}`);
|
|
34617
34705
|
}
|
|
34618
34706
|
if (voice?.enabled) {
|
|
34619
|
-
const
|
|
34707
|
+
const emoState2 = emotionEngine?.getState();
|
|
34708
|
+
const emoCtx2 = emoState2 ? { valence: emoState2.valence, arousal: emoState2.arousal, label: emoState2.label, emoji: emoState2.emoji } : void 0;
|
|
34709
|
+
const desc = describeToolResult(event.toolName ?? "unknown", event.success ?? false, vLevel, event.content ?? void 0, emoCtx2);
|
|
34620
34710
|
if (desc) {
|
|
34621
34711
|
renderVoiceText(desc);
|
|
34622
34712
|
voice.speak(desc);
|
|
@@ -36190,13 +36280,88 @@ ${result.text}`;
|
|
|
36190
36280
|
writeContent(() => renderUserInterrupt(`[Media: ${cleanPath}]`));
|
|
36191
36281
|
}
|
|
36192
36282
|
} else {
|
|
36193
|
-
activeTask.runner.injectUserMessage(input);
|
|
36194
36283
|
const lineCount = input.split("\n").length;
|
|
36195
36284
|
if (lineCount > 1) {
|
|
36196
36285
|
writeContent(() => renderUserInterrupt(`[pasted ${lineCount} lines]`));
|
|
36197
36286
|
} else {
|
|
36198
36287
|
writeContent(() => renderUserInterrupt(input));
|
|
36199
36288
|
}
|
|
36289
|
+
const steerRunner = activeTask.runner;
|
|
36290
|
+
const steerTaskGoal = lastSubmittedPrompt;
|
|
36291
|
+
const steerFeed = getActivityFeed();
|
|
36292
|
+
(async () => {
|
|
36293
|
+
try {
|
|
36294
|
+
const steerBackend = new OllamaAgenticBackend(currentConfig.backendUrl, currentConfig.model, currentConfig.apiKey);
|
|
36295
|
+
const steerAgent = new AgenticRunner(steerBackend, {
|
|
36296
|
+
maxTurns: 3,
|
|
36297
|
+
maxTokens: 512,
|
|
36298
|
+
temperature: 0.3,
|
|
36299
|
+
requestTimeoutMs: 15e3,
|
|
36300
|
+
taskTimeoutMs: 3e4,
|
|
36301
|
+
streamEnabled: false
|
|
36302
|
+
});
|
|
36303
|
+
steerAgent.setWorkingDirectory(repoRoot);
|
|
36304
|
+
steerAgent.registerTool({
|
|
36305
|
+
name: "task_complete",
|
|
36306
|
+
description: "Return the steering instruction for the main agent.",
|
|
36307
|
+
parameters: {
|
|
36308
|
+
type: "object",
|
|
36309
|
+
properties: {
|
|
36310
|
+
summary: {
|
|
36311
|
+
type: "string",
|
|
36312
|
+
description: "The expanded steering instruction to inject into the main agent's context."
|
|
36313
|
+
},
|
|
36314
|
+
acknowledgment: {
|
|
36315
|
+
type: "string",
|
|
36316
|
+
description: "A brief (1 sentence) spoken acknowledgment to the user. Will be spoken aloud via TTS."
|
|
36317
|
+
}
|
|
36318
|
+
},
|
|
36319
|
+
required: ["summary", "acknowledgment"]
|
|
36320
|
+
},
|
|
36321
|
+
async execute(args) {
|
|
36322
|
+
return { success: true, output: JSON.stringify(args) };
|
|
36323
|
+
}
|
|
36324
|
+
});
|
|
36325
|
+
const recentActivity = steerFeed.getSummary(10, false);
|
|
36326
|
+
const steerPrompt = [
|
|
36327
|
+
`The user typed a mid-task message while the agent is working. Your job:`,
|
|
36328
|
+
`1. Understand what the user wants changed/added/corrected`,
|
|
36329
|
+
`2. Produce a brief spoken acknowledgment (1 sentence, conversational)`,
|
|
36330
|
+
`3. Expand their input into a clear, structured steering instruction for the main agent`,
|
|
36331
|
+
``,
|
|
36332
|
+
`Current task goal: "${steerTaskGoal.slice(0, 500)}"`,
|
|
36333
|
+
``,
|
|
36334
|
+
`Recent agent activity:`,
|
|
36335
|
+
recentActivity,
|
|
36336
|
+
``,
|
|
36337
|
+
`User's mid-task message: "${input}"`,
|
|
36338
|
+
``,
|
|
36339
|
+
`Call task_complete with:`,
|
|
36340
|
+
`- acknowledgment: brief spoken response to the user (e.g. "Got it, I'll adjust the approach")`,
|
|
36341
|
+
`- summary: expanded instruction for the main agent (e.g. "USER STEERING: The user wants X instead of Y. Adjust your approach to prioritize Z. Specifically, they are asking you to...")`
|
|
36342
|
+
].join("\n");
|
|
36343
|
+
const result = await steerAgent.run(steerPrompt, "Steering sub-agent \u2014 interpret user input and produce instruction.");
|
|
36344
|
+
let acknowledgment = "Got it, adjusting.";
|
|
36345
|
+
let steering = `USER STEERING: ${input}`;
|
|
36346
|
+
try {
|
|
36347
|
+
const parsed = JSON.parse(result.summary || "{}");
|
|
36348
|
+
if (parsed.acknowledgment)
|
|
36349
|
+
acknowledgment = parsed.acknowledgment;
|
|
36350
|
+
if (parsed.summary)
|
|
36351
|
+
steering = parsed.summary;
|
|
36352
|
+
} catch {
|
|
36353
|
+
if (result.summary)
|
|
36354
|
+
steering = `USER STEERING: ${result.summary}`;
|
|
36355
|
+
}
|
|
36356
|
+
if (voiceEngine?.enabled) {
|
|
36357
|
+
writeContent(() => renderVoiceText(acknowledgment));
|
|
36358
|
+
voiceEngine.speak(acknowledgment);
|
|
36359
|
+
}
|
|
36360
|
+
steerRunner.injectUserMessage(steering);
|
|
36361
|
+
} catch {
|
|
36362
|
+
steerRunner.injectUserMessage(input);
|
|
36363
|
+
}
|
|
36364
|
+
})();
|
|
36200
36365
|
}
|
|
36201
36366
|
showPrompt();
|
|
36202
36367
|
return;
|
package/package.json
CHANGED