open-agents-ai 0.72.0 → 0.73.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/dist/index.js +376 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15097,6 +15097,23 @@ These tools use OS cron (survives process death) and persist state to .oa/ for c
|
|
|
15097
15097
|
Use cron_agent for recurring autonomous tasks, scheduler for simple repeating commands,
|
|
15098
15098
|
reminder for deferred attention, and agenda for strategic focus tracking.
|
|
15099
15099
|
|
|
15100
|
+
## Priority Ingress \u2014 Task Classification & Delegation
|
|
15101
|
+
|
|
15102
|
+
When multiple tasks arrive (Telegram, reminders, updates), classify and route them:
|
|
15103
|
+
- priority_classify: Determine a task's priority (critical/high/moderate/normal/low/salient)
|
|
15104
|
+
priority_classify(message='...', source='external', origin='telegram')
|
|
15105
|
+
Returns: priority, weight, delegable flag, handling policy
|
|
15106
|
+
- priority_delegate: Send normal/low/salient tasks to a sub-agent
|
|
15107
|
+
priority_delegate(task_prompt='...', priority='normal')
|
|
15108
|
+
|
|
15109
|
+
Priority handling policies:
|
|
15110
|
+
CRITICAL (100): Interrupt immediately. Handle now.
|
|
15111
|
+
HIGH (80): Interrupt at turn boundary. Handle next.
|
|
15112
|
+
MODERATE (60): Queue, run after current task.
|
|
15113
|
+
NORMAL (40): Can delegate to sub-agent.
|
|
15114
|
+
LOW (20): Should delegate to sub-agent.
|
|
15115
|
+
SALIENT (5): Note for later, delegate if possible.
|
|
15116
|
+
|
|
15100
15117
|
## Context Efficiency
|
|
15101
15118
|
|
|
15102
15119
|
- Use grep_search to find specific code instead of reading many files
|
|
@@ -33655,6 +33672,216 @@ ${files.map((f) => `- [\`${f}\`](./${f})`).join("\n")}
|
|
|
33655
33672
|
}
|
|
33656
33673
|
});
|
|
33657
33674
|
|
|
33675
|
+
// packages/cli/dist/tui/priority-ingress.js
|
|
33676
|
+
function classifyPriority(prompt, source, origin) {
|
|
33677
|
+
for (const rule of CLASSIFICATION_RULES) {
|
|
33678
|
+
if (rule.sourceType !== "any" && rule.sourceType !== source)
|
|
33679
|
+
continue;
|
|
33680
|
+
if (rule.pattern.test(prompt)) {
|
|
33681
|
+
return { priority: rule.priority, confidence: rule.confidence };
|
|
33682
|
+
}
|
|
33683
|
+
}
|
|
33684
|
+
if (origin === "attention" || origin === "reminder") {
|
|
33685
|
+
return { priority: "moderate", confidence: 0.6 };
|
|
33686
|
+
}
|
|
33687
|
+
if (source === "external" && origin === "telegram") {
|
|
33688
|
+
return { priority: "normal", confidence: 0.65 };
|
|
33689
|
+
}
|
|
33690
|
+
if (origin === "dmn") {
|
|
33691
|
+
return { priority: "low", confidence: 0.7 };
|
|
33692
|
+
}
|
|
33693
|
+
if (origin === "update") {
|
|
33694
|
+
return { priority: "moderate", confidence: 0.85 };
|
|
33695
|
+
}
|
|
33696
|
+
return { priority: "normal", confidence: 0.5 };
|
|
33697
|
+
}
|
|
33698
|
+
var PRIORITY_WEIGHT, PRIORITY_POLICIES, CLASSIFICATION_RULES, PriorityIngressEngine;
|
|
33699
|
+
var init_priority_ingress = __esm({
|
|
33700
|
+
"packages/cli/dist/tui/priority-ingress.js"() {
|
|
33701
|
+
"use strict";
|
|
33702
|
+
PRIORITY_WEIGHT = {
|
|
33703
|
+
critical: 100,
|
|
33704
|
+
high: 80,
|
|
33705
|
+
moderate: 60,
|
|
33706
|
+
normal: 40,
|
|
33707
|
+
low: 20,
|
|
33708
|
+
salient: 5
|
|
33709
|
+
};
|
|
33710
|
+
PRIORITY_POLICIES = {
|
|
33711
|
+
critical: { canInterrupt: true, canDelegate: false, maxWaitMs: 0, notifyAdmin: true },
|
|
33712
|
+
high: { canInterrupt: true, canDelegate: false, maxWaitMs: 0, notifyAdmin: true },
|
|
33713
|
+
moderate: { canInterrupt: false, canDelegate: false, maxWaitMs: 3e4, notifyAdmin: false },
|
|
33714
|
+
normal: { canInterrupt: false, canDelegate: true, maxWaitMs: 6e4, notifyAdmin: false },
|
|
33715
|
+
low: { canInterrupt: false, canDelegate: true, maxWaitMs: 3e5, notifyAdmin: false },
|
|
33716
|
+
salient: { canInterrupt: false, canDelegate: true, maxWaitMs: 9e5, notifyAdmin: false }
|
|
33717
|
+
};
|
|
33718
|
+
CLASSIFICATION_RULES = [
|
|
33719
|
+
// CRITICAL — show-stoppers
|
|
33720
|
+
{ pattern: /\b(security breach|data loss|compromised|urgent.*fix|production down|critical.*failure)\b/i, sourceType: "any", priority: "critical", confidence: 0.95 },
|
|
33721
|
+
{ pattern: /\bCRITICAL\b/, sourceType: "any", priority: "critical", confidence: 0.9 },
|
|
33722
|
+
// HIGH — important interrupts
|
|
33723
|
+
{ pattern: /\b(failing ci|build broken|tests? failing|deployment failed|regression)\b/i, sourceType: "any", priority: "high", confidence: 0.85 },
|
|
33724
|
+
{ pattern: /\b(URGENT|ASAP|immediately|right now)\b/i, sourceType: "external", priority: "high", confidence: 0.8 },
|
|
33725
|
+
// MODERATE — significant but non-interrupt
|
|
33726
|
+
{ pattern: /\b(update available|new version|upgrade)\b/i, sourceType: "any", priority: "moderate", confidence: 0.85 },
|
|
33727
|
+
{ pattern: /\b(admin|owner)\b/i, sourceType: "external", priority: "moderate", confidence: 0.7 },
|
|
33728
|
+
// LOW — background tasks
|
|
33729
|
+
{ pattern: /\b(routine|check|status|health)\b/i, sourceType: "internal", priority: "low", confidence: 0.75 },
|
|
33730
|
+
{ pattern: /\b(FYI|notice|info|heads up)\b/i, sourceType: "any", priority: "salient", confidence: 0.8 }
|
|
33731
|
+
];
|
|
33732
|
+
PriorityIngressEngine = class {
|
|
33733
|
+
queue = [];
|
|
33734
|
+
completedTasks = [];
|
|
33735
|
+
taskCounter = 0;
|
|
33736
|
+
/** Callback: notify admin about high-priority tasks */
|
|
33737
|
+
adminNotify;
|
|
33738
|
+
/** Callback: request attention switch from current task */
|
|
33739
|
+
requestInterrupt;
|
|
33740
|
+
/** Callback: delegate task to sub-agent */
|
|
33741
|
+
delegateToSubAgent;
|
|
33742
|
+
constructor() {
|
|
33743
|
+
}
|
|
33744
|
+
/** Wire callbacks after construction (Telegram/agent may not be ready at construction time) */
|
|
33745
|
+
setAdminNotify(cb) {
|
|
33746
|
+
this.adminNotify = cb;
|
|
33747
|
+
}
|
|
33748
|
+
setInterruptHandler(cb) {
|
|
33749
|
+
this.requestInterrupt = cb;
|
|
33750
|
+
}
|
|
33751
|
+
setDelegationHandler(cb) {
|
|
33752
|
+
this.delegateToSubAgent = cb;
|
|
33753
|
+
}
|
|
33754
|
+
/**
|
|
33755
|
+
* Ingest a new task from any source.
|
|
33756
|
+
* Classifies priority, applies handling policy, and routes accordingly.
|
|
33757
|
+
*/
|
|
33758
|
+
ingest(prompt, source, origin, metadata, overridePriority) {
|
|
33759
|
+
const classification = overridePriority ? { priority: overridePriority, confidence: 1 } : classifyPriority(prompt, source, origin);
|
|
33760
|
+
const policy = PRIORITY_POLICIES[classification.priority];
|
|
33761
|
+
const task = {
|
|
33762
|
+
id: `task-${++this.taskCounter}-${Date.now()}`,
|
|
33763
|
+
source,
|
|
33764
|
+
origin,
|
|
33765
|
+
priority: classification.priority,
|
|
33766
|
+
prompt,
|
|
33767
|
+
metadata,
|
|
33768
|
+
ingressAt: Date.now(),
|
|
33769
|
+
delegable: policy.canDelegate,
|
|
33770
|
+
confidence: classification.confidence,
|
|
33771
|
+
status: "pending"
|
|
33772
|
+
};
|
|
33773
|
+
if (policy.notifyAdmin && this.adminNotify) {
|
|
33774
|
+
const priorityLabel = classification.priority.toUpperCase();
|
|
33775
|
+
const preview = prompt.length > 120 ? prompt.slice(0, 120) + "..." : prompt;
|
|
33776
|
+
this.adminNotify(`[${priorityLabel}] ${origin}: ${preview}`);
|
|
33777
|
+
}
|
|
33778
|
+
if (policy.canInterrupt && this.requestInterrupt) {
|
|
33779
|
+
this.requestInterrupt(task);
|
|
33780
|
+
task.status = "active";
|
|
33781
|
+
} else if (policy.canDelegate && this.delegateToSubAgent) {
|
|
33782
|
+
task.status = "delegated";
|
|
33783
|
+
this.delegateToSubAgent(task).then((summary) => {
|
|
33784
|
+
task.status = "completed";
|
|
33785
|
+
task.completionSummary = summary;
|
|
33786
|
+
this.completedTasks.push(task);
|
|
33787
|
+
}).catch(() => {
|
|
33788
|
+
task.status = "pending";
|
|
33789
|
+
task.priority = "moderate";
|
|
33790
|
+
task.delegable = false;
|
|
33791
|
+
this.queue.push(task);
|
|
33792
|
+
this.sortQueue();
|
|
33793
|
+
});
|
|
33794
|
+
} else {
|
|
33795
|
+
this.queue.push(task);
|
|
33796
|
+
this.sortQueue();
|
|
33797
|
+
}
|
|
33798
|
+
return task;
|
|
33799
|
+
}
|
|
33800
|
+
/**
|
|
33801
|
+
* Get the next task from the priority queue.
|
|
33802
|
+
* Returns null if queue is empty.
|
|
33803
|
+
* Implements weighted scoring: priority_weight + recency_bonus
|
|
33804
|
+
* (REF: Generative Agents scoring — importance + recency)
|
|
33805
|
+
*/
|
|
33806
|
+
dequeue() {
|
|
33807
|
+
if (this.queue.length === 0)
|
|
33808
|
+
return null;
|
|
33809
|
+
const task = this.queue.shift();
|
|
33810
|
+
task.status = "active";
|
|
33811
|
+
return task;
|
|
33812
|
+
}
|
|
33813
|
+
/** Mark a task as completed */
|
|
33814
|
+
complete(taskId, summary) {
|
|
33815
|
+
const idx = this.queue.findIndex((t) => t.id === taskId);
|
|
33816
|
+
if (idx >= 0) {
|
|
33817
|
+
const task = this.queue.splice(idx, 1)[0];
|
|
33818
|
+
task.status = "completed";
|
|
33819
|
+
task.completionSummary = summary;
|
|
33820
|
+
this.completedTasks.push(task);
|
|
33821
|
+
}
|
|
33822
|
+
}
|
|
33823
|
+
/** Mark active task as completed (when we don't track by ID) */
|
|
33824
|
+
completeActive(summary) {
|
|
33825
|
+
this.completedTasks.push({
|
|
33826
|
+
id: `completed-${Date.now()}`,
|
|
33827
|
+
source: "internal",
|
|
33828
|
+
origin: "main",
|
|
33829
|
+
priority: "normal",
|
|
33830
|
+
prompt: "",
|
|
33831
|
+
ingressAt: Date.now(),
|
|
33832
|
+
delegable: false,
|
|
33833
|
+
confidence: 1,
|
|
33834
|
+
status: "completed",
|
|
33835
|
+
completionSummary: summary
|
|
33836
|
+
});
|
|
33837
|
+
}
|
|
33838
|
+
/** Check if there are pending tasks that can interrupt */
|
|
33839
|
+
hasInterruptibleTask() {
|
|
33840
|
+
return this.queue.some((t) => {
|
|
33841
|
+
const policy = PRIORITY_POLICIES[t.priority];
|
|
33842
|
+
return policy.canInterrupt;
|
|
33843
|
+
});
|
|
33844
|
+
}
|
|
33845
|
+
/** Get queue length */
|
|
33846
|
+
get pending() {
|
|
33847
|
+
return this.queue.length;
|
|
33848
|
+
}
|
|
33849
|
+
/** Get stats */
|
|
33850
|
+
getStats() {
|
|
33851
|
+
const byPriority = {
|
|
33852
|
+
critical: 0,
|
|
33853
|
+
high: 0,
|
|
33854
|
+
moderate: 0,
|
|
33855
|
+
normal: 0,
|
|
33856
|
+
low: 0,
|
|
33857
|
+
salient: 0
|
|
33858
|
+
};
|
|
33859
|
+
const bySources = {};
|
|
33860
|
+
for (const t of [...this.queue, ...this.completedTasks]) {
|
|
33861
|
+
byPriority[t.priority]++;
|
|
33862
|
+
bySources[t.origin] = (bySources[t.origin] ?? 0) + 1;
|
|
33863
|
+
}
|
|
33864
|
+
return {
|
|
33865
|
+
pending: this.queue.length,
|
|
33866
|
+
completed: this.completedTasks.length,
|
|
33867
|
+
byPriority,
|
|
33868
|
+
bySources
|
|
33869
|
+
};
|
|
33870
|
+
}
|
|
33871
|
+
/** Sort queue by priority weight (descending), then by recency (FIFO within priority) */
|
|
33872
|
+
sortQueue() {
|
|
33873
|
+
this.queue.sort((a, b) => {
|
|
33874
|
+
const wa = PRIORITY_WEIGHT[a.priority];
|
|
33875
|
+
const wb = PRIORITY_WEIGHT[b.priority];
|
|
33876
|
+
if (wa !== wb)
|
|
33877
|
+
return wb - wa;
|
|
33878
|
+
return a.ingressAt - b.ingressAt;
|
|
33879
|
+
});
|
|
33880
|
+
}
|
|
33881
|
+
};
|
|
33882
|
+
}
|
|
33883
|
+
});
|
|
33884
|
+
|
|
33658
33885
|
// packages/cli/dist/tui/bless-engine.js
|
|
33659
33886
|
function renderBlessStart() {
|
|
33660
33887
|
process.stdout.write(`
|
|
@@ -33683,6 +33910,7 @@ var init_bless_engine = __esm({
|
|
|
33683
33910
|
"use strict";
|
|
33684
33911
|
init_render();
|
|
33685
33912
|
init_dist2();
|
|
33913
|
+
init_priority_ingress();
|
|
33686
33914
|
BlessEngine = class {
|
|
33687
33915
|
config;
|
|
33688
33916
|
repoRoot;
|
|
@@ -33696,6 +33924,8 @@ var init_bless_engine = __esm({
|
|
|
33696
33924
|
};
|
|
33697
33925
|
/** Pending tasks from external sources (telegram, reminders, etc.) */
|
|
33698
33926
|
pendingQueue = [];
|
|
33927
|
+
/** Priority-aware ingress engine */
|
|
33928
|
+
priorityEngine = new PriorityIngressEngine();
|
|
33699
33929
|
constructor(config, repoRoot) {
|
|
33700
33930
|
this.config = config;
|
|
33701
33931
|
this.repoRoot = repoRoot;
|
|
@@ -33739,36 +33969,44 @@ var init_bless_engine = __esm({
|
|
|
33739
33969
|
/** Enqueue a task from an external source (e.g., Telegram) */
|
|
33740
33970
|
enqueueTask(source, prompt, chatId) {
|
|
33741
33971
|
this.pendingQueue.push({ source, prompt, chatId });
|
|
33972
|
+
const taskSource = source === "telegram" || source === "webhook" ? "external" : "internal";
|
|
33973
|
+
this.priorityEngine.ingest(prompt, taskSource, source, chatId != null ? { chatId } : void 0);
|
|
33742
33974
|
}
|
|
33743
|
-
/** Check for pending tasks from any source. Returns next task or null.
|
|
33975
|
+
/** Check for pending tasks from any source. Returns next task or null.
|
|
33976
|
+
* Uses priority-weighted ordering (REF: Nalar arXiv:2601.05109 two-level control).
|
|
33977
|
+
* Priority queue is checked first, then reminders/attention items are ingested. */
|
|
33744
33978
|
async getNextTask() {
|
|
33745
|
-
if (this.pendingQueue.length > 0) {
|
|
33746
|
-
return this.pendingQueue.shift();
|
|
33747
|
-
}
|
|
33748
33979
|
try {
|
|
33749
33980
|
const dueReminders = await getDueReminders(this.repoRoot);
|
|
33750
|
-
const
|
|
33751
|
-
|
|
33752
|
-
const
|
|
33753
|
-
|
|
33754
|
-
source: "reminder",
|
|
33755
|
-
prompt: `URGENT REMINDER: ${r.message}${r.tags?.length ? ` [tags: ${r.tags.join(", ")}]` : ""}. Check the agenda for full context and take appropriate action.`
|
|
33756
|
-
};
|
|
33981
|
+
for (const r of dueReminders.filter((r2) => r2.status === "pending")) {
|
|
33982
|
+
const priority = r.priority === "critical" ? "critical" : r.priority === "high" ? "high" : "moderate";
|
|
33983
|
+
const prompt = `${priority === "critical" ? "URGENT " : ""}REMINDER: ${r.message}${r.tags?.length ? ` [tags: ${r.tags.join(", ")}]` : ""}. Check the agenda for full context and take appropriate action.`;
|
|
33984
|
+
this.priorityEngine.ingest(prompt, "internal", "reminder", { reminderId: r.id }, priority);
|
|
33757
33985
|
}
|
|
33758
33986
|
} catch {
|
|
33759
33987
|
}
|
|
33760
33988
|
try {
|
|
33761
33989
|
const items = await getActiveAttentionItems(this.repoRoot);
|
|
33762
|
-
const
|
|
33763
|
-
|
|
33764
|
-
|
|
33765
|
-
return {
|
|
33766
|
-
source: "attention",
|
|
33767
|
-
prompt: `CRITICAL ATTENTION ITEM: [${a.category}] ${a.title}${a.description ? ": " + a.description : ""}. Investigate and take action.`
|
|
33768
|
-
};
|
|
33990
|
+
for (const a of items.filter((a2) => a2.priority === "critical")) {
|
|
33991
|
+
const prompt = `CRITICAL ATTENTION ITEM: [${a.category}] ${a.title}${a.description ? ": " + a.description : ""}. Investigate and take action.`;
|
|
33992
|
+
this.priorityEngine.ingest(prompt, "internal", "attention", void 0, "critical");
|
|
33769
33993
|
}
|
|
33770
33994
|
} catch {
|
|
33771
33995
|
}
|
|
33996
|
+
if (this.pendingQueue.length > 0) {
|
|
33997
|
+
const item = this.pendingQueue.shift();
|
|
33998
|
+
const classification = classifyPriority(item.prompt, "external", item.source);
|
|
33999
|
+
return { ...item, priority: classification.priority };
|
|
34000
|
+
}
|
|
34001
|
+
const task = this.priorityEngine.dequeue();
|
|
34002
|
+
if (task) {
|
|
34003
|
+
return {
|
|
34004
|
+
source: task.origin,
|
|
34005
|
+
prompt: task.prompt,
|
|
34006
|
+
chatId: task.metadata?.chatId,
|
|
34007
|
+
priority: task.priority
|
|
34008
|
+
};
|
|
34009
|
+
}
|
|
33772
34010
|
return null;
|
|
33773
34011
|
}
|
|
33774
34012
|
/** Record that a task was processed */
|
|
@@ -35079,7 +35317,7 @@ function appraiseEvent(event) {
|
|
|
35079
35317
|
function clamp(value, min, max) {
|
|
35080
35318
|
return Math.max(min, Math.min(max, value));
|
|
35081
35319
|
}
|
|
35082
|
-
var BASELINE_VALENCE, BASELINE_AROUSAL, DECAY_HALF_LIFE_MS, LABEL_UPDATE_INTERVAL_MS, EXCITEMENT_THRESHOLD, DISTRESS_THRESHOLD, OUTREACH_COOLDOWN_MS, LABEL_REGEN_THRESHOLD, EmotionEngine;
|
|
35320
|
+
var BASELINE_VALENCE, BASELINE_AROUSAL, DECAY_HALF_LIFE_MS, LABEL_UPDATE_INTERVAL_MS, EXCITEMENT_THRESHOLD, DISTRESS_THRESHOLD, OUTREACH_COOLDOWN_MS, OUTREACH_MIN_STREAK, LABEL_REGEN_THRESHOLD, EmotionEngine;
|
|
35083
35321
|
var init_emotion_engine = __esm({
|
|
35084
35322
|
"packages/cli/dist/tui/emotion-engine.js"() {
|
|
35085
35323
|
"use strict";
|
|
@@ -35090,9 +35328,10 @@ var init_emotion_engine = __esm({
|
|
|
35090
35328
|
LABEL_UPDATE_INTERVAL_MS = 15e3;
|
|
35091
35329
|
EXCITEMENT_THRESHOLD = 0.85;
|
|
35092
35330
|
DISTRESS_THRESHOLD = -0.7;
|
|
35093
|
-
OUTREACH_COOLDOWN_MS =
|
|
35331
|
+
OUTREACH_COOLDOWN_MS = 9e5;
|
|
35332
|
+
OUTREACH_MIN_STREAK = 5;
|
|
35094
35333
|
LABEL_REGEN_THRESHOLD = 0.06;
|
|
35095
|
-
EmotionEngine = class {
|
|
35334
|
+
EmotionEngine = class _EmotionEngine {
|
|
35096
35335
|
state = {
|
|
35097
35336
|
valence: BASELINE_VALENCE,
|
|
35098
35337
|
arousal: BASELINE_AROUSAL,
|
|
@@ -35112,9 +35351,24 @@ var init_emotion_engine = __esm({
|
|
|
35112
35351
|
consecutiveFailures = 0;
|
|
35113
35352
|
consecutiveSuccesses = 0;
|
|
35114
35353
|
totalEvents = 0;
|
|
35354
|
+
/** Ring buffer of recent tool activity for contextual outreach messages */
|
|
35355
|
+
recentTools = [];
|
|
35356
|
+
static MAX_RECENT_TOOLS = 8;
|
|
35357
|
+
/** Current task description set by the TUI for outreach context */
|
|
35358
|
+
currentTask = "";
|
|
35359
|
+
/** Files touched in current session (for outreach context) */
|
|
35360
|
+
filesTouched = /* @__PURE__ */ new Set();
|
|
35115
35361
|
constructor(config) {
|
|
35116
35362
|
this.config = config;
|
|
35117
35363
|
}
|
|
35364
|
+
/** Set the current task description for contextual outreach messages */
|
|
35365
|
+
setCurrentTask(description) {
|
|
35366
|
+
this.currentTask = description;
|
|
35367
|
+
}
|
|
35368
|
+
/** Record a file that was modified (for outreach context) */
|
|
35369
|
+
trackFile(filePath) {
|
|
35370
|
+
this.filesTouched.add(filePath);
|
|
35371
|
+
}
|
|
35118
35372
|
/** Get the current emotional state (with decay applied) */
|
|
35119
35373
|
getState() {
|
|
35120
35374
|
this.applyDecay();
|
|
@@ -35158,6 +35412,18 @@ ${behavioralHint}`;
|
|
|
35158
35412
|
this.consecutiveSuccesses = 0;
|
|
35159
35413
|
}
|
|
35160
35414
|
}
|
|
35415
|
+
if (event.type === "tool_call" && event.toolName) {
|
|
35416
|
+
this.recentTools.push({ name: event.toolName });
|
|
35417
|
+
if (this.recentTools.length > _EmotionEngine.MAX_RECENT_TOOLS) {
|
|
35418
|
+
this.recentTools.shift();
|
|
35419
|
+
}
|
|
35420
|
+
if ((event.toolName === "file_write" || event.toolName === "file_edit") && event.toolArgs?.path) {
|
|
35421
|
+
this.trackFile(String(event.toolArgs.path));
|
|
35422
|
+
}
|
|
35423
|
+
}
|
|
35424
|
+
if (event.type === "tool_result" && this.recentTools.length > 0) {
|
|
35425
|
+
this.recentTools[this.recentTools.length - 1].success = event.success;
|
|
35426
|
+
}
|
|
35161
35427
|
let momentum = 1;
|
|
35162
35428
|
if (this.consecutiveSuccesses >= 2) {
|
|
35163
35429
|
momentum = 1 + (this.consecutiveSuccesses - 1) * 0.2;
|
|
@@ -35197,6 +35463,9 @@ ${behavioralHint}`;
|
|
|
35197
35463
|
};
|
|
35198
35464
|
this.consecutiveFailures = 0;
|
|
35199
35465
|
this.consecutiveSuccesses = 0;
|
|
35466
|
+
this.recentTools = [];
|
|
35467
|
+
this.filesTouched.clear();
|
|
35468
|
+
this.currentTask = "";
|
|
35200
35469
|
this.config.onEmotionUpdate?.(this.getState());
|
|
35201
35470
|
}
|
|
35202
35471
|
// ── Private ────────────────────────────────────────────────────────────
|
|
@@ -35285,30 +35554,98 @@ Example: \u{1F30A} flowing`;
|
|
|
35285
35554
|
const now = Date.now();
|
|
35286
35555
|
if (now - this.lastOutreach < OUTREACH_COOLDOWN_MS)
|
|
35287
35556
|
return;
|
|
35288
|
-
const { valence, arousal, emoji
|
|
35557
|
+
const { valence, arousal, emoji } = this.state;
|
|
35289
35558
|
if (arousal >= EXCITEMENT_THRESHOLD && valence > 0.5) {
|
|
35290
|
-
|
|
35291
|
-
|
|
35292
|
-
|
|
35293
|
-
|
|
35294
|
-
message += ` ${this.consecutiveSuccesses} things went right in a row!`;
|
|
35295
|
-
}
|
|
35559
|
+
const isTaskComplete = event.type === "complete";
|
|
35560
|
+
const isSignificantStreak = this.consecutiveSuccesses >= OUTREACH_MIN_STREAK;
|
|
35561
|
+
if (!isTaskComplete && !isSignificantStreak)
|
|
35562
|
+
return;
|
|
35296
35563
|
this.lastOutreach = now;
|
|
35297
|
-
this.config.onAdminOutreach(
|
|
35564
|
+
this.config.onAdminOutreach(this.composeOutreachMessage("positive", event));
|
|
35298
35565
|
return;
|
|
35299
35566
|
}
|
|
35300
35567
|
if (valence <= DISTRESS_THRESHOLD && arousal > 0.6) {
|
|
35301
|
-
|
|
35302
|
-
|
|
35303
|
-
message += ` ${this.consecutiveFailures} consecutive failures \u2014 might need guidance.`;
|
|
35304
|
-
} else if (event.type === "error") {
|
|
35305
|
-
message += " Encountered an error.";
|
|
35306
|
-
}
|
|
35568
|
+
if (this.consecutiveFailures < 3 && event.type !== "error")
|
|
35569
|
+
return;
|
|
35307
35570
|
this.lastOutreach = now;
|
|
35308
|
-
this.config.onAdminOutreach(
|
|
35571
|
+
this.config.onAdminOutreach(this.composeOutreachMessage("negative", event));
|
|
35309
35572
|
return;
|
|
35310
35573
|
}
|
|
35311
35574
|
}
|
|
35575
|
+
/**
|
|
35576
|
+
* Compose a rich, conversational outreach message with real context
|
|
35577
|
+
* instead of raw "Feeling {label}!" spam.
|
|
35578
|
+
*/
|
|
35579
|
+
composeOutreachMessage(tone, event) {
|
|
35580
|
+
const { emoji } = this.state;
|
|
35581
|
+
const parts = [];
|
|
35582
|
+
if (tone === "positive") {
|
|
35583
|
+
if (event.type === "complete" && event.content) {
|
|
35584
|
+
const summary = event.content.length > 200 ? event.content.slice(0, 200) + "..." : event.content;
|
|
35585
|
+
parts.push(`${emoji} Task complete: ${summary}`);
|
|
35586
|
+
} else if (this.consecutiveSuccesses >= OUTREACH_MIN_STREAK) {
|
|
35587
|
+
const activity = this.describeRecentActivity();
|
|
35588
|
+
parts.push(`${emoji} On a roll \u2014 ${this.consecutiveSuccesses} operations succeeded.${activity ? ` ${activity}` : ""}`);
|
|
35589
|
+
}
|
|
35590
|
+
if (this.currentTask && event.type !== "complete") {
|
|
35591
|
+
parts.push(`Working on: ${this.currentTask}`);
|
|
35592
|
+
}
|
|
35593
|
+
if (this.filesTouched.size > 0) {
|
|
35594
|
+
const files = [...this.filesTouched];
|
|
35595
|
+
const shown = files.slice(-3).map((f) => {
|
|
35596
|
+
const segments = f.split("/");
|
|
35597
|
+
return segments.length > 2 ? segments.slice(-2).join("/") : f;
|
|
35598
|
+
});
|
|
35599
|
+
const fileStr = shown.join(", ");
|
|
35600
|
+
parts.push(this.filesTouched.size > 3 ? `Modified ${this.filesTouched.size} files (${fileStr}...)` : `Modified: ${fileStr}`);
|
|
35601
|
+
}
|
|
35602
|
+
} else {
|
|
35603
|
+
if (this.consecutiveFailures >= 3) {
|
|
35604
|
+
const activity = this.describeRecentActivity();
|
|
35605
|
+
parts.push(`${emoji} Hit a wall \u2014 ${this.consecutiveFailures} consecutive failures.${activity ? ` Last: ${activity}` : ""}`);
|
|
35606
|
+
} else if (event.type === "error" && event.content) {
|
|
35607
|
+
const errSnippet = event.content.length > 150 ? event.content.slice(0, 150) + "..." : event.content;
|
|
35608
|
+
parts.push(`${emoji} Error encountered: ${errSnippet}`);
|
|
35609
|
+
} else {
|
|
35610
|
+
parts.push(`${emoji} Struggling with the current task.`);
|
|
35611
|
+
}
|
|
35612
|
+
if (this.currentTask) {
|
|
35613
|
+
parts.push(`Working on: ${this.currentTask}`);
|
|
35614
|
+
}
|
|
35615
|
+
if (this.consecutiveFailures >= 5) {
|
|
35616
|
+
parts.push("May need guidance or a different approach.");
|
|
35617
|
+
}
|
|
35618
|
+
}
|
|
35619
|
+
return parts.join("\n");
|
|
35620
|
+
}
|
|
35621
|
+
/** Summarize recent tool activity into a brief phrase */
|
|
35622
|
+
describeRecentActivity() {
|
|
35623
|
+
if (this.recentTools.length === 0)
|
|
35624
|
+
return "";
|
|
35625
|
+
const counts = /* @__PURE__ */ new Map();
|
|
35626
|
+
for (const t of this.recentTools) {
|
|
35627
|
+
counts.set(t.name, (counts.get(t.name) ?? 0) + 1);
|
|
35628
|
+
}
|
|
35629
|
+
const descriptions = [];
|
|
35630
|
+
if (counts.has("file_edit") || counts.has("file_write")) {
|
|
35631
|
+
descriptions.push("editing code");
|
|
35632
|
+
}
|
|
35633
|
+
if (counts.has("shell")) {
|
|
35634
|
+
descriptions.push("running commands");
|
|
35635
|
+
}
|
|
35636
|
+
if (counts.has("grep_search") || counts.has("glob_find")) {
|
|
35637
|
+
descriptions.push("searching codebase");
|
|
35638
|
+
}
|
|
35639
|
+
if (counts.has("web_fetch") || counts.has("web_search")) {
|
|
35640
|
+
descriptions.push("researching");
|
|
35641
|
+
}
|
|
35642
|
+
if (counts.has("memory_write") || counts.has("memory_read")) {
|
|
35643
|
+
descriptions.push("updating memory");
|
|
35644
|
+
}
|
|
35645
|
+
if (descriptions.length === 0)
|
|
35646
|
+
return "";
|
|
35647
|
+
return descriptions.length === 1 ? `Currently ${descriptions[0]}.` : `Currently ${descriptions.slice(0, -1).join(", ")} and ${descriptions[descriptions.length - 1]}.`;
|
|
35648
|
+
}
|
|
35312
35649
|
};
|
|
35313
35650
|
}
|
|
35314
35651
|
});
|
|
@@ -40041,6 +40378,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
40041
40378
|
}
|
|
40042
40379
|
writeContent(() => renderUserMessage(`/${cmdResult.name}${cmdResult.args ? " " + cmdResult.args : ""}`));
|
|
40043
40380
|
lastSubmittedPrompt = skillPrompt;
|
|
40381
|
+
emotionEngine.setCurrentTask(`/${cmdResult.name}${cmdResult.args ? " " + cmdResult.args.slice(0, 80) : ""}`);
|
|
40044
40382
|
try {
|
|
40045
40383
|
statusBar.setProcessing(true);
|
|
40046
40384
|
const task = startTask(skillPrompt, currentConfig, repoRoot, voiceEngine, {
|
|
@@ -40219,6 +40557,8 @@ Summarize or analyze this transcription as appropriate.`;
|
|
|
40219
40557
|
const displayText = isImage ? `[Image: ${cleanPath}]` : inputLineCount > 1 ? `[pasted ${inputLineCount} lines]` : fullInput;
|
|
40220
40558
|
writeContent(() => renderUserMessage(displayText));
|
|
40221
40559
|
lastSubmittedPrompt = fullInput;
|
|
40560
|
+
const taskPreview = fullInput.length > 100 ? fullInput.slice(0, 100) + "..." : fullInput;
|
|
40561
|
+
emotionEngine.setCurrentTask(taskPreview);
|
|
40222
40562
|
try {
|
|
40223
40563
|
const memSnippets = gatherMemorySnippets(repoRoot);
|
|
40224
40564
|
if (memSnippets.length > 0) {
|
package/package.json
CHANGED