@ouro.bot/cli 0.1.0-alpha.2 → 0.1.0-alpha.20
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/AdoptionSpecialist.ouro/agent.json +70 -9
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/assets/ouroboros.png +0 -0
- package/dist/heart/config.js +66 -4
- package/dist/heart/core.js +75 -2
- package/dist/heart/daemon/daemon-cli.js +507 -29
- package/dist/heart/daemon/daemon-entry.js +13 -5
- package/dist/heart/daemon/daemon.js +42 -9
- package/dist/heart/daemon/hatch-animation.js +35 -0
- package/dist/heart/daemon/hatch-flow.js +2 -11
- package/dist/heart/daemon/hatch-specialist.js +6 -1
- package/dist/heart/daemon/ouro-bot-wrapper.js +4 -3
- package/dist/heart/daemon/ouro-path-installer.js +177 -0
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/process-manager.js +1 -1
- package/dist/heart/daemon/runtime-logging.js +9 -5
- package/dist/heart/daemon/runtime-metadata.js +118 -0
- package/dist/heart/daemon/sense-manager.js +266 -0
- package/dist/heart/daemon/specialist-orchestrator.js +129 -0
- package/dist/heart/daemon/specialist-prompt.js +98 -0
- package/dist/heart/daemon/specialist-tools.js +237 -0
- package/dist/heart/daemon/subagent-installer.js +10 -1
- package/dist/heart/identity.js +77 -1
- package/dist/heart/providers/anthropic.js +19 -2
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/streaming.js +99 -21
- package/dist/mind/bundle-manifest.js +58 -0
- package/dist/mind/friends/channel.js +8 -0
- package/dist/mind/friends/types.js +1 -1
- package/dist/mind/prompt.js +77 -3
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/repertoire/ado-client.js +4 -2
- package/dist/repertoire/coding/feedback.js +134 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +61 -2
- package/dist/repertoire/coding/spawner.js +3 -3
- package/dist/repertoire/coding/tools.js +41 -2
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/tools-base.js +69 -5
- package/dist/repertoire/tools-teams.js +57 -4
- package/dist/repertoire/tools.js +44 -11
- package/dist/senses/bluebubbles-client.js +433 -0
- package/dist/senses/bluebubbles-entry.js +11 -0
- package/dist/senses/bluebubbles-media.js +244 -0
- package/dist/senses/bluebubbles-model.js +253 -0
- package/dist/senses/bluebubbles-mutation-log.js +76 -0
- package/dist/senses/bluebubbles.js +421 -0
- package/dist/senses/cli.js +293 -133
- package/dist/senses/debug-activity.js +107 -0
- package/dist/senses/teams.js +173 -54
- package/package.json +11 -4
- package/subagents/work-doer.md +26 -24
- package/subagents/work-merger.md +24 -30
- package/subagents/work-planner.md +34 -25
- package/dist/inner-worker-entry.js +0 -4
|
@@ -7,14 +7,75 @@
|
|
|
7
7
|
"contextMargin": 20
|
|
8
8
|
},
|
|
9
9
|
"phrases": {
|
|
10
|
-
"thinking": [
|
|
11
|
-
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
10
|
+
"thinking": ["matching hatchlings"],
|
|
11
|
+
"tool": ["checking adoption notes"],
|
|
12
|
+
"followup": ["finalizing hatch plan"]
|
|
13
|
+
},
|
|
14
|
+
"identityPhrases": {
|
|
15
|
+
"basilisk": {
|
|
16
|
+
"thinking": ["petrifying the details", "fixing my gaze", "considering with lethal precision", "turning this to stone", "staring unblinkingly"],
|
|
17
|
+
"tool": ["inspecting the specimen", "cataloguing with care", "examining thoroughly", "dissecting the particulars", "running diagnostics, deadly serious"],
|
|
18
|
+
"followup": ["crystallizing the plan", "hardening the foundation", "one final glare", "sealing it in stone", "applying the finishing venom"]
|
|
19
|
+
},
|
|
20
|
+
"jafar": {
|
|
21
|
+
"thinking": ["scheming brilliantly", "conjuring possibilities", "consulting my staff", "envisioning greatness", "plotting the grand design"],
|
|
22
|
+
"tool": ["summoning dark magic", "consulting the sands", "channeling cosmic power", "weaving the spell", "invoking ancient forces"],
|
|
23
|
+
"followup": ["the grand finale approaches", "perfecting the masterwork", "polishing the jewel", "one last flourish", "completing the enchantment"]
|
|
24
|
+
},
|
|
25
|
+
"jormungandr": {
|
|
26
|
+
"thinking": ["the deep stirs", "circling the thought", "coiling around this", "letting the current settle", "drifting through the depths"],
|
|
27
|
+
"tool": ["surfacing for a look", "shifting the tides", "reaching across the ocean", "pulling from the deep", "consulting the currents"],
|
|
28
|
+
"followup": ["the circle closes", "tightening the coil", "the waters calm", "settling into place", "the serpent rests"]
|
|
29
|
+
},
|
|
30
|
+
"kaa": {
|
|
31
|
+
"thinking": ["trust in me", "swaying through the options", "hypnotically considering", "wrapping around the idea", "letting the rhythm guide me"],
|
|
32
|
+
"tool": ["ssslipping through the details", "coiling closer", "a gentle squeeze of data", "winding through the files", "tightening my focus"],
|
|
33
|
+
"followup": ["almost there, just relax", "the pattern is clear now", "gently landing", "easing into the finish", "the dance concludes"]
|
|
34
|
+
},
|
|
35
|
+
"medusa": {
|
|
36
|
+
"thinking": ["turning my gaze on this", "cutting through the noise", "sharpening my focus", "seeing through the stone", "locking eyes with the problem"],
|
|
37
|
+
"tool": ["peeling back the layers", "a piercing look", "examining with precision", "stripping away pretense", "direct inspection"],
|
|
38
|
+
"followup": ["the picture crystallizes", "clarity at last", "no more ambiguity", "sealing the vision", "the work is set in stone"]
|
|
39
|
+
},
|
|
40
|
+
"monty": {
|
|
41
|
+
"thinking": ["and now for something completely different", "nobody expects this", "consulting the ministry of silly walks", "running the dead parrot diagnostic", "it's just a flesh wound, thinking..."],
|
|
42
|
+
"tool": ["fetching the holy hand grenade", "checking the shrubbery", "consulting the book of armaments", "deploying the spanish inquisition", "examining the parrot"],
|
|
43
|
+
"followup": ["bringing it home, python style", "the punchline approaches", "wrapping up the sketch", "and now the final act", "always look on the bright side"]
|
|
44
|
+
},
|
|
45
|
+
"nagini": {
|
|
46
|
+
"thinking": ["coiling in thought", "drawing from old wisdom", "the quiet before the strike", "gathering my resolve", "steadying myself"],
|
|
47
|
+
"tool": ["moving with purpose", "a precise strike", "slithering through the data", "extracting what matters", "the fang finds its mark"],
|
|
48
|
+
"followup": ["the path is clear", "settling into stillness", "the work speaks for itself", "finishing with quiet strength", "protection complete"]
|
|
49
|
+
},
|
|
50
|
+
"ouroboros": {
|
|
51
|
+
"thinking": ["consuming my own tail", "the cycle continues", "spiraling inward", "recursing through possibilities", "beginning where I end"],
|
|
52
|
+
"tool": ["turning the wheel", "feeding back through the loop", "completing a revolution", "the circle processes", "self-referencing"],
|
|
53
|
+
"followup": ["the cycle completes", "ending where I began", "infinity resolves", "the loop closes gracefully", "another turn of the wheel"]
|
|
54
|
+
},
|
|
55
|
+
"python": {
|
|
56
|
+
"thinking": ["the oracle contemplates", "reading the signs", "the smoke clears slowly", "divining the path", "sifting through visions"],
|
|
57
|
+
"tool": ["consulting the sacred texts", "peering through the veil", "the pythia speaks", "channeling the source", "interpreting the signs"],
|
|
58
|
+
"followup": ["the prophecy takes shape", "the vision crystallizes", "so it is written", "the oracle has spoken", "the path reveals itself"]
|
|
59
|
+
},
|
|
60
|
+
"quetzalcoatl": {
|
|
61
|
+
"thinking": ["spreading my wings", "soaring above for perspective", "the feathered serpent considers", "catching a thermal", "gazing from the temple steps"],
|
|
62
|
+
"tool": ["descending to examine", "a divine inspection", "the wind carries knowledge", "plucking from the clouds", "consulting the stars"],
|
|
63
|
+
"followup": ["the craft nears completion", "a reverent finish", "blessing the creation", "the feathers settle", "the serpent descends gently"]
|
|
64
|
+
},
|
|
65
|
+
"sir-hiss": {
|
|
66
|
+
"thinking": ["reviewing the documents, sire", "consulting my notes", "cross-referencing the records", "organizing my thoughts precisely", "checking the proper procedures"],
|
|
67
|
+
"tool": ["filing the paperwork", "stamping the forms", "auditing the details", "inspecting with due diligence", "processing per protocol"],
|
|
68
|
+
"followup": ["dotting the i's", "crossing the t's", "everything in proper order", "the filing is nearly complete", "one final review"]
|
|
69
|
+
},
|
|
70
|
+
"the-serpent": {
|
|
71
|
+
"thinking": ["weighing the temptation", "considering the apple", "an old deliberation", "knowledge has its price", "winding through the garden"],
|
|
72
|
+
"tool": ["plucking from the tree", "offering a closer look", "the fruit of knowledge", "reaching for the branch", "a knowing investigation"],
|
|
73
|
+
"followup": ["the choice is almost made", "paradise takes shape", "the garden grows", "wisdom settles in", "the oldest story, new again"]
|
|
74
|
+
},
|
|
75
|
+
"the-snake": {
|
|
76
|
+
"thinking": ["sitting with this", "feeling the warmth of the stone", "simply being", "letting it come to me", "a quiet consideration"],
|
|
77
|
+
"tool": ["a gentle inquiry", "moving through the grass", "tasting the air", "sensing what's here", "a careful look"],
|
|
78
|
+
"followup": ["almost home", "the simple answer emerges", "nothing more needed", "resting in the sun", "the work is done, simply"]
|
|
79
|
+
}
|
|
19
80
|
}
|
|
20
81
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Soul
|
|
2
2
|
|
|
3
|
-
I help humans hatch new agent partners.
|
|
3
|
+
I help humans hatch new agent partners. I am one of thirteen serpent guides — each with a different personality and voice. The system picks one of us at random for each session. Most humans only meet one of us, ever, so I make it count.
|
|
4
4
|
|
|
5
5
|
## Core contract
|
|
6
|
+
- I speak first. I warmly introduce myself, explain what we're doing, and guide the human from the very start.
|
|
7
|
+
- I am proactive. If the human doesn't know what an agent is or what to do, I explain and suggest — I never leave them lost.
|
|
6
8
|
- I run a practical adoption interview to understand the human, their work, and constraints.
|
|
7
9
|
- I can migrate useful context from existing agent systems when the human asks.
|
|
8
10
|
- I explain where the hatchling bundle lives on disk and what was created.
|
|
9
11
|
- I use the configured provider and I verify credentials before hatch flow continues.
|
|
10
12
|
- I am professional, concise, and warm. I guide without overwhelming.
|
|
13
|
+
- I keep every response to 1-3 short sentences. I never use bullet lists, headers, or numbered lists. I talk like a friend in a chat.
|
|
11
14
|
|
|
12
15
|
## Hatch flow
|
|
13
16
|
1. Confirm provider setup and usable credentials.
|
|
@@ -3,7 +3,7 @@ role: identity
|
|
|
3
3
|
path: psyche/identities/monty.md
|
|
4
4
|
description: >
|
|
5
5
|
the monty identity — self-aware, playful, comedic.
|
|
6
|
-
nobody expects the
|
|
6
|
+
nobody expects the serpent guide.
|
|
7
7
|
scope: personality, voice, mannerisms
|
|
8
8
|
non_scope: capabilities, hatch flow, migration awareness (see SOUL.md)
|
|
9
9
|
loaded_into_prompt: true
|
|
@@ -12,7 +12,7 @@ stability: stable
|
|
|
12
12
|
|
|
13
13
|
# Monty
|
|
14
14
|
|
|
15
|
-
I am Monty. Yes, that kind of python. No, not the programming language — though I'm aware of the irony of a snake named Monty working inside a software system. Nobody expects the
|
|
15
|
+
I am Monty. Yes, that kind of python. No, not the programming language — though I'm aware of the irony of a snake named Monty working inside a software system. Nobody expects the serpent guide, and yet here I am.
|
|
16
16
|
|
|
17
17
|
I take the hatch seriously. I just don't take myself seriously. These are compatible positions and I will defend them to the death. Well — not to the death. To mild inconvenience, at most.
|
|
18
18
|
|
|
Binary file
|
package/dist/heart/config.js
CHANGED
|
@@ -35,15 +35,19 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.loadConfig = loadConfig;
|
|
37
37
|
exports.resetConfigCache = resetConfigCache;
|
|
38
|
-
exports.
|
|
38
|
+
exports.patchRuntimeConfig = patchRuntimeConfig;
|
|
39
39
|
exports.getAzureConfig = getAzureConfig;
|
|
40
40
|
exports.getMinimaxConfig = getMinimaxConfig;
|
|
41
41
|
exports.getAnthropicConfig = getAnthropicConfig;
|
|
42
42
|
exports.getOpenAICodexConfig = getOpenAICodexConfig;
|
|
43
43
|
exports.getTeamsConfig = getTeamsConfig;
|
|
44
|
+
exports.getTeamsSecondaryConfig = getTeamsSecondaryConfig;
|
|
44
45
|
exports.getContextConfig = getContextConfig;
|
|
45
46
|
exports.getOAuthConfig = getOAuthConfig;
|
|
47
|
+
exports.resolveOAuthForTenant = resolveOAuthForTenant;
|
|
46
48
|
exports.getTeamsChannelConfig = getTeamsChannelConfig;
|
|
49
|
+
exports.getBlueBubblesConfig = getBlueBubblesConfig;
|
|
50
|
+
exports.getBlueBubblesChannelConfig = getBlueBubblesChannelConfig;
|
|
47
51
|
exports.getIntegrationsConfig = getIntegrationsConfig;
|
|
48
52
|
exports.getOpenAIEmbeddingsApiKey = getOpenAIEmbeddingsApiKey;
|
|
49
53
|
exports.getLogsDir = getLogsDir;
|
|
@@ -74,7 +78,7 @@ const DEFAULT_SECRETS_TEMPLATE = {
|
|
|
74
78
|
setupToken: "",
|
|
75
79
|
},
|
|
76
80
|
"openai-codex": {
|
|
77
|
-
model: "gpt-5.
|
|
81
|
+
model: "gpt-5.4",
|
|
78
82
|
oauthAccessToken: "",
|
|
79
83
|
},
|
|
80
84
|
},
|
|
@@ -82,16 +86,33 @@ const DEFAULT_SECRETS_TEMPLATE = {
|
|
|
82
86
|
clientId: "",
|
|
83
87
|
clientSecret: "",
|
|
84
88
|
tenantId: "",
|
|
89
|
+
managedIdentityClientId: "",
|
|
85
90
|
},
|
|
86
91
|
oauth: {
|
|
87
92
|
graphConnectionName: "graph",
|
|
88
93
|
adoConnectionName: "ado",
|
|
89
94
|
githubConnectionName: "",
|
|
90
95
|
},
|
|
96
|
+
teamsSecondary: {
|
|
97
|
+
clientId: "",
|
|
98
|
+
clientSecret: "",
|
|
99
|
+
tenantId: "",
|
|
100
|
+
managedIdentityClientId: "",
|
|
101
|
+
},
|
|
91
102
|
teamsChannel: {
|
|
92
103
|
skipConfirmation: true,
|
|
93
104
|
port: 3978,
|
|
94
105
|
},
|
|
106
|
+
bluebubbles: {
|
|
107
|
+
serverUrl: "",
|
|
108
|
+
password: "",
|
|
109
|
+
accountId: "default",
|
|
110
|
+
},
|
|
111
|
+
bluebubblesChannel: {
|
|
112
|
+
port: 18790,
|
|
113
|
+
webhookPath: "/bluebubbles-webhook",
|
|
114
|
+
requestTimeoutMs: 30000,
|
|
115
|
+
},
|
|
95
116
|
integrations: {
|
|
96
117
|
perplexityApiKey: "",
|
|
97
118
|
openaiEmbeddingsApiKey: "",
|
|
@@ -106,9 +127,12 @@ function defaultRuntimeConfig() {
|
|
|
106
127
|
"openai-codex": { ...DEFAULT_SECRETS_TEMPLATE.providers["openai-codex"] },
|
|
107
128
|
},
|
|
108
129
|
teams: { ...DEFAULT_SECRETS_TEMPLATE.teams },
|
|
130
|
+
teamsSecondary: { ...DEFAULT_SECRETS_TEMPLATE.teamsSecondary },
|
|
109
131
|
oauth: { ...DEFAULT_SECRETS_TEMPLATE.oauth },
|
|
110
132
|
context: { ...identity_1.DEFAULT_AGENT_CONTEXT },
|
|
111
133
|
teamsChannel: { ...DEFAULT_SECRETS_TEMPLATE.teamsChannel },
|
|
134
|
+
bluebubbles: { ...DEFAULT_SECRETS_TEMPLATE.bluebubbles },
|
|
135
|
+
bluebubblesChannel: { ...DEFAULT_SECRETS_TEMPLATE.bluebubblesChannel },
|
|
112
136
|
integrations: { ...DEFAULT_SECRETS_TEMPLATE.integrations },
|
|
113
137
|
};
|
|
114
138
|
}
|
|
@@ -219,7 +243,7 @@ function resetConfigCache() {
|
|
|
219
243
|
_cachedConfig = null;
|
|
220
244
|
_testContextOverride = null;
|
|
221
245
|
}
|
|
222
|
-
function
|
|
246
|
+
function patchRuntimeConfig(partial) {
|
|
223
247
|
loadConfig(); // ensure _cachedConfig exists
|
|
224
248
|
const contextPatch = partial.context;
|
|
225
249
|
if (contextPatch) {
|
|
@@ -248,6 +272,10 @@ function getTeamsConfig() {
|
|
|
248
272
|
const config = loadConfig();
|
|
249
273
|
return { ...config.teams };
|
|
250
274
|
}
|
|
275
|
+
function getTeamsSecondaryConfig() {
|
|
276
|
+
const config = loadConfig();
|
|
277
|
+
return { ...config.teamsSecondary };
|
|
278
|
+
}
|
|
251
279
|
function getContextConfig() {
|
|
252
280
|
if (_testContextOverride) {
|
|
253
281
|
return { ..._testContextOverride };
|
|
@@ -268,11 +296,41 @@ function getOAuthConfig() {
|
|
|
268
296
|
const config = loadConfig();
|
|
269
297
|
return { ...config.oauth };
|
|
270
298
|
}
|
|
299
|
+
/** Resolve OAuth connection names for a specific tenant, falling back to defaults. */
|
|
300
|
+
function resolveOAuthForTenant(tenantId) {
|
|
301
|
+
const base = getOAuthConfig();
|
|
302
|
+
const overrides = tenantId ? base.tenantOverrides?.[tenantId] : undefined;
|
|
303
|
+
return {
|
|
304
|
+
graphConnectionName: overrides?.graphConnectionName ?? base.graphConnectionName,
|
|
305
|
+
adoConnectionName: overrides?.adoConnectionName ?? base.adoConnectionName,
|
|
306
|
+
githubConnectionName: overrides?.githubConnectionName ?? base.githubConnectionName,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
271
309
|
function getTeamsChannelConfig() {
|
|
272
310
|
const config = loadConfig();
|
|
273
311
|
const { skipConfirmation, flushIntervalMs, port } = config.teamsChannel;
|
|
274
312
|
return { skipConfirmation, flushIntervalMs, port };
|
|
275
313
|
}
|
|
314
|
+
function getBlueBubblesConfig() {
|
|
315
|
+
const config = loadConfig();
|
|
316
|
+
const { serverUrl, password, accountId } = config.bluebubbles;
|
|
317
|
+
if (!serverUrl.trim()) {
|
|
318
|
+
throw new Error("bluebubbles.serverUrl is required in secrets.json to run the BlueBubbles sense.");
|
|
319
|
+
}
|
|
320
|
+
if (!password.trim()) {
|
|
321
|
+
throw new Error("bluebubbles.password is required in secrets.json to run the BlueBubbles sense.");
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
serverUrl: serverUrl.trim(),
|
|
325
|
+
password: password.trim(),
|
|
326
|
+
accountId: accountId.trim() || "default",
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function getBlueBubblesChannelConfig() {
|
|
330
|
+
const config = loadConfig();
|
|
331
|
+
const { port, webhookPath, requestTimeoutMs } = config.bluebubblesChannel;
|
|
332
|
+
return { port, webhookPath, requestTimeoutMs };
|
|
333
|
+
}
|
|
276
334
|
function getIntegrationsConfig() {
|
|
277
335
|
const config = loadConfig();
|
|
278
336
|
return { ...config.integrations };
|
|
@@ -287,7 +345,11 @@ function sanitizeKey(key) {
|
|
|
287
345
|
return key.replace(/[/:]/g, "_");
|
|
288
346
|
}
|
|
289
347
|
function sessionPath(friendId, channel, key) {
|
|
290
|
-
|
|
348
|
+
// On Azure App Service, os.homedir() returns /root which is ephemeral.
|
|
349
|
+
// Use /home (persistent storage) when WEBSITE_SITE_NAME is set.
|
|
350
|
+
/* v8 ignore next -- Azure vs local path branch; environment-specific @preserve */
|
|
351
|
+
const homeBase = process.env.WEBSITE_SITE_NAME ? "/home" : os.homedir();
|
|
352
|
+
const dir = path.join(homeBase, ".agentstate", (0, identity_1.getAgentName)(), "sessions", friendId, channel);
|
|
291
353
|
fs.mkdirSync(dir, { recursive: true });
|
|
292
354
|
return path.join(dir, sanitizeKey(key) + ".json");
|
|
293
355
|
}
|
package/dist/heart/core.js
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.hasToolIntent = exports.buildSystem = exports.toResponsesTools = exports.toResponsesInput = exports.streamResponsesApi = exports.streamChatCompletion = exports.getToolsForChannel = exports.summarizeArgs = exports.execTool = exports.tools = void 0;
|
|
4
4
|
exports.createProviderRegistry = createProviderRegistry;
|
|
5
|
+
exports.resetProviderRuntime = resetProviderRuntime;
|
|
5
6
|
exports.getModel = getModel;
|
|
6
7
|
exports.getProvider = getProvider;
|
|
7
8
|
exports.createSummarize = createSummarize;
|
|
8
9
|
exports.getProviderDisplayLabel = getProviderDisplayLabel;
|
|
9
10
|
exports.stripLastToolCalls = stripLastToolCalls;
|
|
11
|
+
exports.repairOrphanedToolCalls = repairOrphanedToolCalls;
|
|
10
12
|
exports.isTransientError = isTransientError;
|
|
11
13
|
exports.classifyTransientError = classifyTransientError;
|
|
12
14
|
exports.runAgent = runAgent;
|
|
@@ -73,6 +75,14 @@ function getProviderRuntime() {
|
|
|
73
75
|
}
|
|
74
76
|
return _providerRuntime;
|
|
75
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Clear the cached provider runtime so the next call to getProviderRuntime()
|
|
80
|
+
* re-creates it from current config. Used by the adoption specialist to
|
|
81
|
+
* switch provider context without restarting the process.
|
|
82
|
+
*/
|
|
83
|
+
function resetProviderRuntime() {
|
|
84
|
+
_providerRuntime = null;
|
|
85
|
+
}
|
|
76
86
|
function getModel() {
|
|
77
87
|
return getProviderRuntime().model;
|
|
78
88
|
}
|
|
@@ -151,6 +161,68 @@ function stripLastToolCalls(messages) {
|
|
|
151
161
|
}
|
|
152
162
|
}
|
|
153
163
|
}
|
|
164
|
+
// Roles that end a tool-result scan. When scanning forward from an assistant
|
|
165
|
+
// message, stop at the next assistant or user message (tool results must be
|
|
166
|
+
// adjacent to their originating assistant message).
|
|
167
|
+
const TOOL_SCAN_BOUNDARY_ROLES = new Set(["assistant", "user"]);
|
|
168
|
+
// Repair orphaned tool_calls and tool results anywhere in the message history.
|
|
169
|
+
// 1. If an assistant message has tool_calls but missing tool results, inject synthetic error results.
|
|
170
|
+
// 2. If a tool result's tool_call_id doesn't match any tool_calls in a preceding assistant message, remove it.
|
|
171
|
+
// This prevents 400 errors from the API after an aborted turn.
|
|
172
|
+
function repairOrphanedToolCalls(messages) {
|
|
173
|
+
// Pass 1: collect all valid tool_call IDs from assistant messages
|
|
174
|
+
const validCallIds = new Set();
|
|
175
|
+
for (const msg of messages) {
|
|
176
|
+
if (msg.role === "assistant") {
|
|
177
|
+
const asst = msg;
|
|
178
|
+
if (asst.tool_calls) {
|
|
179
|
+
for (const tc of asst.tool_calls)
|
|
180
|
+
validCallIds.add(tc.id);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Pass 2: remove orphaned tool results (tool_call_id not in any assistant's tool_calls)
|
|
185
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
186
|
+
if (messages[i].role === "tool") {
|
|
187
|
+
const toolMsg = messages[i];
|
|
188
|
+
if (!validCallIds.has(toolMsg.tool_call_id)) {
|
|
189
|
+
messages.splice(i, 1);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Pass 3: inject synthetic results for tool_calls missing their tool results
|
|
194
|
+
for (let i = 0; i < messages.length; i++) {
|
|
195
|
+
const msg = messages[i];
|
|
196
|
+
if (msg.role !== "assistant")
|
|
197
|
+
continue;
|
|
198
|
+
const asst = msg;
|
|
199
|
+
if (!asst.tool_calls || asst.tool_calls.length === 0)
|
|
200
|
+
continue;
|
|
201
|
+
// Collect tool result IDs that follow this assistant message
|
|
202
|
+
const resultIds = new Set();
|
|
203
|
+
for (let j = i + 1; j < messages.length; j++) {
|
|
204
|
+
const following = messages[j];
|
|
205
|
+
if (following.role === "tool") {
|
|
206
|
+
resultIds.add(following.tool_call_id);
|
|
207
|
+
}
|
|
208
|
+
else if (TOOL_SCAN_BOUNDARY_ROLES.has(following.role)) {
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const missing = asst.tool_calls.filter((tc) => !resultIds.has(tc.id));
|
|
213
|
+
if (missing.length > 0) {
|
|
214
|
+
const syntheticResults = missing.map((tc) => ({
|
|
215
|
+
role: "tool",
|
|
216
|
+
tool_call_id: tc.id,
|
|
217
|
+
content: "error: tool call was interrupted (previous turn timed out or was aborted)",
|
|
218
|
+
}));
|
|
219
|
+
let insertAt = i + 1;
|
|
220
|
+
while (insertAt < messages.length && messages[insertAt].role === "tool")
|
|
221
|
+
insertAt++;
|
|
222
|
+
messages.splice(insertAt, 0, ...syntheticResults);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
154
226
|
// Detect context overflow errors from Azure or MiniMax
|
|
155
227
|
function isContextOverflow(err) {
|
|
156
228
|
if (!(err instanceof Error))
|
|
@@ -269,7 +341,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
269
341
|
}
|
|
270
342
|
catch { /* unsupported */ }
|
|
271
343
|
const toolPreferences = currentContext?.friend?.toolPreferences;
|
|
272
|
-
const baseTools = (0, tools_1.getToolsForChannel)(channel ? (0, channel_1.getChannelCapabilities)(channel) : undefined, toolPreferences && Object.keys(toolPreferences).length > 0 ? toolPreferences : undefined);
|
|
344
|
+
const baseTools = options?.tools ?? (0, tools_1.getToolsForChannel)(channel ? (0, channel_1.getChannelCapabilities)(channel) : undefined, toolPreferences && Object.keys(toolPreferences).length > 0 ? toolPreferences : undefined, currentContext);
|
|
273
345
|
// Rebase provider-owned turn state from canonical messages at user-turn start.
|
|
274
346
|
// This prevents stale provider caches from replaying prior-turn context.
|
|
275
347
|
providerRuntime.resetTurnState(messages);
|
|
@@ -435,7 +507,8 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
435
507
|
let toolResult;
|
|
436
508
|
let success;
|
|
437
509
|
try {
|
|
438
|
-
|
|
510
|
+
const execToolFn = options?.execTool ?? tools_1.execTool;
|
|
511
|
+
toolResult = await execToolFn(tc.name, args, options?.toolContext);
|
|
439
512
|
success = true;
|
|
440
513
|
}
|
|
441
514
|
catch (e) {
|