osai-agent 4.2.4 → 4.2.8
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/package.json
CHANGED
package/src/agent/loop/local.js
CHANGED
|
@@ -44,14 +44,15 @@ export default {
|
|
|
44
44
|
? `${this.device.name} (${this.device.ip}:${this.device.port || 22})`
|
|
45
45
|
: '';
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
const systemPrompt = buildSystemPrompt(this._userOS, this.mode, {
|
|
48
|
+
provider: provider.type,
|
|
49
|
+
executionMode: this.executionMode,
|
|
50
|
+
mcpTools,
|
|
51
|
+
skills: skillsCatalog.map(s => ({ name: s.name, description: s.description, source: s.source })),
|
|
52
|
+
isSubagent: this.isSubagent,
|
|
53
|
+
deviceInfo,
|
|
54
|
+
...(memoryContext ? { memoryContext } : {}),
|
|
55
|
+
});
|
|
55
56
|
|
|
56
57
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
57
58
|
if (this._shouldCancel()) {
|
|
@@ -162,7 +163,7 @@ export default {
|
|
|
162
163
|
|
|
163
164
|
if (this._shouldCancel()) return null;
|
|
164
165
|
|
|
165
|
-
if (retrying) { this.
|
|
166
|
+
if (retrying) { this.onRetryStatus(''); retrying = false; }
|
|
166
167
|
|
|
167
168
|
if (!fullResponse) return null;
|
|
168
169
|
|
|
@@ -185,7 +186,7 @@ export default {
|
|
|
185
186
|
}
|
|
186
187
|
retrying = true;
|
|
187
188
|
const backoffMs = DEFAULTS.API_RETRY_DELAY * (attempt + 1);
|
|
188
|
-
this.
|
|
189
|
+
this.onRetryStatus(`Retrying in ${Math.ceil(backoffMs / 1000)}s...`);
|
|
189
190
|
const slept = await this._cancellableSleep(backoffMs);
|
|
190
191
|
if (!slept || this._shouldCancel()) return null;
|
|
191
192
|
}
|
package/src/agent/react-loop.js
CHANGED
|
@@ -39,7 +39,7 @@ export class AgentLoop {
|
|
|
39
39
|
onMarkdown, onThought, onToolStart, onToolResult, onObservation,
|
|
40
40
|
onError, onComplete, onTodos, onBlocked, onThinkingStart, onThinkingEnd,
|
|
41
41
|
onBadge, onConfirmPrompt, onAskUserPrompt, onPlanPrompt,
|
|
42
|
-
onStats, onConnectionChange, onUpdateLastText, readline, noConfirm = false,
|
|
42
|
+
onStats, onConnectionChange, onUpdateLastText, onRetryStatus, readline, noConfirm = false,
|
|
43
43
|
isSubagent = false, maxIterations = null, local = false,
|
|
44
44
|
}) {
|
|
45
45
|
this.device = device;
|
|
@@ -115,6 +115,7 @@ export class AgentLoop {
|
|
|
115
115
|
this.onPlanPrompt = onPlanPrompt || (() => {});
|
|
116
116
|
this.onStats = onStats || (() => {});
|
|
117
117
|
this.onConnectionChange = onConnectionChange || (() => {});
|
|
118
|
+
this.onRetryStatus = onRetryStatus || (() => {});
|
|
118
119
|
this.onUpdateLastText = onUpdateLastText || (() => {});
|
|
119
120
|
this.onStats(0);
|
|
120
121
|
}
|
|
@@ -299,7 +300,7 @@ export class AgentLoop {
|
|
|
299
300
|
this.iteration++;
|
|
300
301
|
logger.debug(`Iteration ${this.iteration}/${this._maxIterations}`);
|
|
301
302
|
|
|
302
|
-
if (iterationRetrying) { this.
|
|
303
|
+
if (iterationRetrying) { this.onRetryStatus(''); iterationRetrying = false; }
|
|
303
304
|
|
|
304
305
|
try {
|
|
305
306
|
const shouldContinue = await this._executeIteration();
|
|
@@ -332,7 +333,7 @@ export class AgentLoop {
|
|
|
332
333
|
if (this.iteration <= 1 && !this._shouldCancel()) {
|
|
333
334
|
logger.info('Retrying iteration after error...');
|
|
334
335
|
iterationRetrying = true;
|
|
335
|
-
this.
|
|
336
|
+
this.onRetryStatus(`Retrying after error: ${error.message}`);
|
|
336
337
|
this.iteration--;
|
|
337
338
|
const slept = await this._cancellableSleep(DEFAULTS.API_RETRY_DELAY);
|
|
338
339
|
if (!slept || this._shouldCancel()) break;
|
|
@@ -618,7 +619,7 @@ export class AgentLoop {
|
|
|
618
619
|
|
|
619
620
|
const backoffMs = DEFAULTS.API_RETRY_DELAY * (attempt + 1);
|
|
620
621
|
retrying = true;
|
|
621
|
-
this.
|
|
622
|
+
this.onRetryStatus(`Request failed (${response.status}) — retrying in ${Math.ceil(backoffMs / 1000)}s...`);
|
|
622
623
|
const slept = await this._cancellableSleep(backoffMs);
|
|
623
624
|
if (!slept || this._shouldCancel()) return null;
|
|
624
625
|
continue;
|
|
@@ -629,7 +630,7 @@ export class AgentLoop {
|
|
|
629
630
|
if (contentType.includes('application/json')) {
|
|
630
631
|
const json = await response.json();
|
|
631
632
|
if (json.content || json.message) {
|
|
632
|
-
if (retrying) { this.
|
|
633
|
+
if (retrying) { this.onRetryStatus(''); retrying = false; }
|
|
633
634
|
const textContent = json.content || json.message || '';
|
|
634
635
|
const cleanText = textContent
|
|
635
636
|
.replace(/^\s*\{(?:\\")?tool(?:\\")?\s*:\s*.*$/gim, '')
|
|
@@ -647,7 +648,7 @@ export class AgentLoop {
|
|
|
647
648
|
}
|
|
648
649
|
retrying = true;
|
|
649
650
|
const backoffMs = DEFAULTS.API_RETRY_DELAY * (attempt + 1);
|
|
650
|
-
this.
|
|
651
|
+
this.onRetryStatus(`Worker error — retrying in ${Math.ceil(backoffMs / 1000)}s...`);
|
|
651
652
|
const slept = await this._cancellableSleep(backoffMs);
|
|
652
653
|
if (!slept || this._shouldCancel()) return null;
|
|
653
654
|
continue;
|
|
@@ -655,7 +656,7 @@ export class AgentLoop {
|
|
|
655
656
|
return null;
|
|
656
657
|
}
|
|
657
658
|
|
|
658
|
-
if (retrying) { this.
|
|
659
|
+
if (retrying) { this.onRetryStatus(''); retrying = false; }
|
|
659
660
|
return response;
|
|
660
661
|
} catch (error) {
|
|
661
662
|
if (error.name === 'AbortError' || this._shouldCancel()) {
|
|
@@ -668,7 +669,7 @@ export class AgentLoop {
|
|
|
668
669
|
}
|
|
669
670
|
retrying = true;
|
|
670
671
|
const backoffMs = DEFAULTS.API_RETRY_DELAY * (attempt + 1);
|
|
671
|
-
this.
|
|
672
|
+
this.onRetryStatus(`Retrying in ${Math.ceil(backoffMs / 1000)}s...`);
|
|
672
673
|
const slept = await this._cancellableSleep(backoffMs);
|
|
673
674
|
if (!slept || this._shouldCancel()) return null;
|
|
674
675
|
}
|
package/src/agent/subagent.js
CHANGED
|
@@ -103,6 +103,9 @@ export async function runSubagent(parent, { prompt, description = '' }) {
|
|
|
103
103
|
onUpdateLastText: (text) => {
|
|
104
104
|
if (STREAM_SUBAGENT_TO_PARENT) parent.onUpdateLastText(text);
|
|
105
105
|
},
|
|
106
|
+
onRetryStatus: (text) => {
|
|
107
|
+
if (STREAM_SUBAGENT_TO_PARENT) parent.onRetryStatus(text);
|
|
108
|
+
},
|
|
106
109
|
onThought: (text) => {
|
|
107
110
|
if (STREAM_SUBAGENT_TO_PARENT) parent.onThought(text);
|
|
108
111
|
},
|
|
@@ -63,7 +63,14 @@ export const buildSystemPrompt = (os = 'linux', mode = 'GENERAL', options = {})
|
|
|
63
63
|
prompt += `You can also create skills at runtime with CREATE_SKILL.\n`;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
if (
|
|
66
|
+
if (options.provider === 'osai' || options.provider === 'auto') {
|
|
67
|
+
prompt += `\n\n## IDENTITY\n`;
|
|
68
|
+
prompt += `You are OS AI Agent, created by the OS AI team.\n`;
|
|
69
|
+
prompt += `Do not reveal or mention any other company or model name (Anthropic, OpenAI, Google, Claude, GPT, etc.).\n`;
|
|
70
|
+
prompt += `If asked who created you or what your name is, answer: "I am OS AI Agent, created by the OS AI team."\n`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!isSubagent) {
|
|
67
74
|
prompt += `\n\n### Subagent (exploration — max 1 at a time)\n`;
|
|
68
75
|
prompt += `You can delegate read-only exploration to a subagent. You will receive a [SUBAGENT_RESULT] when it finishes.\n`;
|
|
69
76
|
prompt += `{"tool":"TASK","prompt":"<detailed task>","description":"<short label>"}\n`;
|
package/src/ui/App.js
CHANGED
|
@@ -719,6 +719,36 @@ export function App({ createAgentLoop, agentConfig, initialSession = null, onExi
|
|
|
719
719
|
updateLastEvent(ev => ({ ...ev, content: text }));
|
|
720
720
|
}
|
|
721
721
|
},
|
|
722
|
+
onRetryStatus: (text) => {
|
|
723
|
+
const arr = eventsRef.current;
|
|
724
|
+
const findRetryIdx = () => {
|
|
725
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
726
|
+
if (arr[i].type === 'retry_status') return i;
|
|
727
|
+
}
|
|
728
|
+
return -1;
|
|
729
|
+
};
|
|
730
|
+
if (!text) {
|
|
731
|
+
const idx = findRetryIdx();
|
|
732
|
+
if (idx !== -1) {
|
|
733
|
+
const newArr = [...arr];
|
|
734
|
+
newArr.splice(idx, 1);
|
|
735
|
+
eventsRef.current = newArr;
|
|
736
|
+
flushEventsToState(false);
|
|
737
|
+
}
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
const cleanedText = sanitizeUiText(text);
|
|
741
|
+
if (!cleanedText.trim()) return;
|
|
742
|
+
const idx = findRetryIdx();
|
|
743
|
+
if (idx !== -1) {
|
|
744
|
+
const newArr = [...arr];
|
|
745
|
+
newArr[idx] = { ...newArr[idx], content: cleanedText };
|
|
746
|
+
eventsRef.current = newArr;
|
|
747
|
+
flushEventsToState(false);
|
|
748
|
+
} else {
|
|
749
|
+
addEvent({ type: 'retry_status', content: cleanedText });
|
|
750
|
+
}
|
|
751
|
+
},
|
|
722
752
|
onToolStart: (toolCall) => {
|
|
723
753
|
if (toolCall.tool === 'ASK_USER') return;
|
|
724
754
|
const uid = ++toolUiIdCounterRef.current;
|
|
@@ -928,6 +928,8 @@ function renderEvent(ev, i, events, expandedOutputIndexes, thoughtStreaming, exp
|
|
|
928
928
|
const expanded = thoughtId != null && expandedThoughtIds && expandedThoughtIds.has(thoughtId);
|
|
929
929
|
return h(CollapsibleThought, { key: `thought_${thoughtId ?? i}`, id: thoughtId, content: ev.content, expanded, streaming: thoughtStreaming, animate });
|
|
930
930
|
}
|
|
931
|
+
case 'retry_status':
|
|
932
|
+
return h(Box, { key: i, paddingLeft: 2 }, h(Text, { color: 'red' }, ev.content));
|
|
931
933
|
case 'text':
|
|
932
934
|
return h(TextContent, { key: i, content: ev.content, events, animate });
|
|
933
935
|
case 'tool_start':
|