nexus-agents 2.72.0 → 2.72.1

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.
@@ -67,7 +67,7 @@ import {
67
67
  clampTaskTtl,
68
68
  getAvailabilityCache,
69
69
  resolveFallback
70
- } from "./chunk-XHVDKY3X.js";
70
+ } from "./chunk-YOREAPF6.js";
71
71
  import {
72
72
  DEFAULTS
73
73
  } from "./chunk-2MD5MWCK.js";
@@ -14309,6 +14309,733 @@ async function loadAllExternalPacks(packs, logger55) {
14309
14309
  return { loaded, errors };
14310
14310
  }
14311
14311
 
14312
+ // src/config/model-identity.ts
14313
+ var VENDOR_PATTERNS = [
14314
+ { vendor: "anthropic", regex: /\b(claude|anthropic)\b/ },
14315
+ // OpenAI: gpt, o1-o9 reasoning, chatgpt, openai prefix
14316
+ { vendor: "openai", regex: /\b(gpt|o[1-9]|chatgpt|openai)\b/ },
14317
+ { vendor: "google", regex: /\b(gemini|bison|gecko|palm|google)\b/ },
14318
+ { vendor: "meta", regex: /\b(llama|meta-llama|meta)\b/ },
14319
+ { vendor: "qwen", regex: /\b(qwen)\b/ },
14320
+ { vendor: "nvidia", regex: /\b(nemotron|nvidia)\b/ },
14321
+ { vendor: "mistral", regex: /\b(mistral|mixtral|codestral)\b/ },
14322
+ { vendor: "cohere", regex: /\b(command-r|command|cohere)\b/ },
14323
+ { vendor: "deepseek", regex: /\b(deepseek)\b/ }
14324
+ ];
14325
+ var FAMILY_PATTERNS = [
14326
+ // Anthropic
14327
+ { vendor: "anthropic", family: "claude-opus", regex: /\b(opus)\b/ },
14328
+ { vendor: "anthropic", family: "claude-sonnet", regex: /\b(sonnet)\b/ },
14329
+ { vendor: "anthropic", family: "claude-haiku", regex: /\b(haiku)\b/ },
14330
+ // OpenAI
14331
+ { vendor: "openai", family: "o-reasoning", regex: /\bo[1-9]\b/ },
14332
+ { vendor: "openai", family: "gpt-4o", regex: /\b(gpt-4o|gpt4o|4o)\b/ },
14333
+ { vendor: "openai", family: "gpt-4", regex: /\b(gpt-4)\b/ },
14334
+ { vendor: "openai", family: "gpt-3.5", regex: /\b(gpt-3-5|gpt-3\.5|gpt35)\b/ },
14335
+ // Google
14336
+ { vendor: "google", family: "gemini-pro", regex: /\bgemini.*\bpro\b/ },
14337
+ { vendor: "google", family: "gemini-flash", regex: /\bgemini.*\bflash\b/ },
14338
+ { vendor: "google", family: "gemini", regex: /\bgemini\b/ },
14339
+ // Meta
14340
+ { vendor: "meta", family: "llama-3", regex: /\bllama-?3\b/ },
14341
+ { vendor: "meta", family: "llama-2", regex: /\bllama-?2\b/ },
14342
+ // Qwen — version is in the family name
14343
+ { vendor: "qwen", family: "qwen-3", regex: /\bqwen-?3\b/ },
14344
+ { vendor: "qwen", family: "qwen-2.5", regex: /\bqwen-?2-?5\b/ },
14345
+ { vendor: "qwen", family: "qwen-2", regex: /\bqwen-?2\b/ },
14346
+ // Mistral
14347
+ { vendor: "mistral", family: "mixtral", regex: /\bmixtral\b/ },
14348
+ { vendor: "mistral", family: "codestral", regex: /\bcodestral\b/ },
14349
+ { vendor: "mistral", family: "mistral", regex: /\bmistral\b/ }
14350
+ ];
14351
+ var QUIRK_PATTERNS = [
14352
+ { regex: /\b(embedding|embed)\b/, quirk: "embedding" },
14353
+ { regex: /\b(thinking|reasoning)\b/, quirk: "thinking" },
14354
+ { regex: /\bvision\b/, quirk: "vision" },
14355
+ { regex: /\b(coder|code)\b/, quirk: "coder" },
14356
+ { regex: /\binstruct\b/, quirk: "instruct" },
14357
+ { regex: /\b(mini|nano|tiny|small|lite)\b/, quirk: "small" },
14358
+ { regex: /\b(large|xl|big|maxi)\b/, quirk: "large" },
14359
+ { regex: /\bhigh\b/, quirk: "high-variant" },
14360
+ { regex: /\b(\d+)b\b/, quirk: "sized-suffix" },
14361
+ // 7b, 70b, 405b
14362
+ { regex: /\b\d{8}\b/, quirk: "dated" }
14363
+ // 20240806 etc
14364
+ ];
14365
+ async function resolveModelIdentity(adapter, options = {}) {
14366
+ const rawModelId = adapter.modelId;
14367
+ const hints = options.hints ?? {};
14368
+ let probe;
14369
+ if (options.skipProbe !== true && typeof adapter.listModels === "function") {
14370
+ probe = await runProbe(adapter, rawModelId);
14371
+ }
14372
+ const parsed = parseModelId(rawModelId);
14373
+ return mergeIdentity({ rawModelId, hints, probe, parsed });
14374
+ }
14375
+ function resolveModelIdentitySync(modelId, hints) {
14376
+ return mergeIdentity({
14377
+ rawModelId: modelId,
14378
+ hints: hints ?? {},
14379
+ probe: void 0,
14380
+ parsed: parseModelId(modelId)
14381
+ });
14382
+ }
14383
+ function parseModelId(modelId) {
14384
+ const normalised = normaliseModelId(modelId);
14385
+ const vendor = detectVendor(normalised);
14386
+ const family = vendor !== void 0 ? detectFamily(normalised, vendor) : void 0;
14387
+ const version = vendor !== void 0 && family !== void 0 ? extractVersion(normalised, family) : void 0;
14388
+ const quirks = detectQuirks(normalised);
14389
+ return {
14390
+ ...vendor !== void 0 && { vendor },
14391
+ ...family !== void 0 && { family },
14392
+ ...version !== void 0 && { version },
14393
+ quirks
14394
+ };
14395
+ }
14396
+ function normaliseModelId(modelId) {
14397
+ return modelId.toLowerCase().replace(/[_/]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
14398
+ }
14399
+ function detectVendor(normalised) {
14400
+ for (const { vendor, regex } of VENDOR_PATTERNS) {
14401
+ if (regex.test(normalised)) return vendor;
14402
+ }
14403
+ return void 0;
14404
+ }
14405
+ function detectFamily(normalised, vendor) {
14406
+ for (const fp of FAMILY_PATTERNS) {
14407
+ if (fp.vendor !== vendor) continue;
14408
+ if (fp.regex.test(normalised)) return fp.family;
14409
+ }
14410
+ return void 0;
14411
+ }
14412
+ function extractVersion(normalised, family) {
14413
+ const familyRoot = family.replace(/^claude-|^gemini-|^llama-|^qwen-|^gpt-/, "");
14414
+ const idx = normalised.indexOf(familyRoot);
14415
+ if (idx === -1) return void 0;
14416
+ const tail = normalised.slice(idx + familyRoot.length);
14417
+ const m = /^[-]?(\d[\d.\-]*)/.exec(tail);
14418
+ if (m === null) return void 0;
14419
+ return m[1]?.replace(/-+$/, "") ?? void 0;
14420
+ }
14421
+ function detectQuirks(normalised) {
14422
+ const out = [];
14423
+ for (const { regex, quirk } of QUIRK_PATTERNS) {
14424
+ if (regex.test(normalised) && !out.includes(quirk)) out.push(quirk);
14425
+ }
14426
+ return out;
14427
+ }
14428
+ async function runProbe(adapter, modelId) {
14429
+ try {
14430
+ const capable = adapter;
14431
+ const models = await capable.listModels();
14432
+ const matched = models.find((m) => m.id === modelId);
14433
+ if (matched === void 0) return void 0;
14434
+ const result = { metadata: matched };
14435
+ if (matched.ownedBy !== void 0) {
14436
+ const vendor = vendorFromOwnedBy(matched.ownedBy);
14437
+ if (vendor !== void 0) {
14438
+ return { ...result, vendor };
14439
+ }
14440
+ }
14441
+ return result;
14442
+ } catch {
14443
+ return void 0;
14444
+ }
14445
+ }
14446
+ var OWNED_BY_PATTERNS = [
14447
+ { substr: "anthropic", vendor: "anthropic" },
14448
+ { substr: "openai", vendor: "openai" },
14449
+ { substr: "google", vendor: "google" },
14450
+ { substr: "meta", vendor: "meta" },
14451
+ { substr: "alibaba", vendor: "qwen" },
14452
+ { substr: "qwen", vendor: "qwen" },
14453
+ { substr: "nvidia", vendor: "nvidia" },
14454
+ { substr: "nemotron", vendor: "nvidia" },
14455
+ { substr: "mistral", vendor: "mistral" },
14456
+ { substr: "cohere", vendor: "cohere" },
14457
+ { substr: "deepseek", vendor: "deepseek" }
14458
+ ];
14459
+ function vendorFromOwnedBy(ownedBy) {
14460
+ const lc = ownedBy.toLowerCase();
14461
+ for (const { substr, vendor } of OWNED_BY_PATTERNS) {
14462
+ if (lc.includes(substr)) return vendor;
14463
+ }
14464
+ return void 0;
14465
+ }
14466
+ function mergeIdentity(args) {
14467
+ const { rawModelId, hints, probe, parsed } = args;
14468
+ const version = pickVersion(hints, parsed);
14469
+ return {
14470
+ vendor: hints.vendor ?? probe?.vendor ?? parsed.vendor ?? "unknown",
14471
+ family: hints.family ?? parsed.family ?? "unknown",
14472
+ ...version !== void 0 && { version },
14473
+ quirks: [.../* @__PURE__ */ new Set([...hints.quirks ?? [], ...parsed.quirks])],
14474
+ source: pickSource(hints, probe, parsed),
14475
+ rawModelId
14476
+ };
14477
+ }
14478
+ function pickVersion(hints, parsed) {
14479
+ return hints.version ?? parsed.version;
14480
+ }
14481
+ function pickSource(hints, probe, parsed) {
14482
+ if (hints.vendor !== void 0) return "modelHints";
14483
+ if (probe?.vendor !== void 0) return "probe";
14484
+ if (parsed.vendor !== void 0) return "modelIdParse";
14485
+ return "default";
14486
+ }
14487
+
14488
+ // src/config/model-behavior-profile.ts
14489
+ var DEFAULT_PROFILE = {
14490
+ parallelToolCalls: false,
14491
+ promptCaching: "none",
14492
+ toolDefinitionFormat: "openai",
14493
+ maxRecommendedTurnBudget: 10,
14494
+ strictJson: true,
14495
+ quirks: [],
14496
+ profileId: "default"
14497
+ };
14498
+ var VENDOR_PROFILES = {
14499
+ anthropic: {
14500
+ profileId: "anthropic-default",
14501
+ promptCaching: "ephemeral",
14502
+ toolDefinitionFormat: "anthropic",
14503
+ parallelToolCalls: true,
14504
+ // Claude 4.x supports parallel tool_use blocks
14505
+ maxRecommendedTurnBudget: 15
14506
+ },
14507
+ openai: {
14508
+ profileId: "openai-default",
14509
+ parallelToolCalls: true,
14510
+ toolDefinitionFormat: "openai",
14511
+ maxRecommendedTurnBudget: 15
14512
+ },
14513
+ google: {
14514
+ profileId: "google-default",
14515
+ toolDefinitionFormat: "gemini",
14516
+ parallelToolCalls: true,
14517
+ maxRecommendedTurnBudget: 15
14518
+ },
14519
+ meta: {
14520
+ profileId: "meta-default",
14521
+ toolDefinitionFormat: "openai",
14522
+ parallelToolCalls: false,
14523
+ // many open-weight Llamas struggle with parallel tools
14524
+ maxRecommendedTurnBudget: 8
14525
+ },
14526
+ qwen: {
14527
+ profileId: "qwen-default",
14528
+ toolDefinitionFormat: "openai",
14529
+ parallelToolCalls: false,
14530
+ maxRecommendedTurnBudget: 8
14531
+ },
14532
+ nvidia: {
14533
+ profileId: "nvidia-nemotron-default",
14534
+ toolDefinitionFormat: "openai",
14535
+ parallelToolCalls: false,
14536
+ maxRecommendedTurnBudget: 8
14537
+ },
14538
+ mistral: {
14539
+ profileId: "mistral-default",
14540
+ toolDefinitionFormat: "openai",
14541
+ parallelToolCalls: false,
14542
+ maxRecommendedTurnBudget: 8
14543
+ },
14544
+ cohere: {
14545
+ profileId: "cohere-default",
14546
+ toolDefinitionFormat: "openai",
14547
+ parallelToolCalls: false,
14548
+ maxRecommendedTurnBudget: 8
14549
+ },
14550
+ deepseek: {
14551
+ profileId: "deepseek-default",
14552
+ toolDefinitionFormat: "openai",
14553
+ parallelToolCalls: false,
14554
+ maxRecommendedTurnBudget: 10
14555
+ }
14556
+ };
14557
+ var FAMILY_PROFILES = [
14558
+ {
14559
+ vendor: "anthropic",
14560
+ family: "claude-opus",
14561
+ override: { profileId: "claude-opus", maxRecommendedTurnBudget: 20 }
14562
+ },
14563
+ {
14564
+ vendor: "anthropic",
14565
+ family: "claude-haiku",
14566
+ override: { profileId: "claude-haiku", maxRecommendedTurnBudget: 8 }
14567
+ },
14568
+ {
14569
+ vendor: "openai",
14570
+ family: "o-reasoning",
14571
+ override: {
14572
+ profileId: "openai-o-reasoning",
14573
+ // o1-style reasoning models burn extra turns thinking; recommend
14574
+ // a higher budget. They also don't support `temperature`.
14575
+ maxRecommendedTurnBudget: 25
14576
+ }
14577
+ },
14578
+ {
14579
+ vendor: "google",
14580
+ family: "gemini-flash",
14581
+ override: { profileId: "gemini-flash", maxRecommendedTurnBudget: 8 }
14582
+ }
14583
+ ];
14584
+ function lookupModelProfile(identity) {
14585
+ let profile = { ...DEFAULT_PROFILE };
14586
+ const vendorOverride = VENDOR_PROFILES[identity.vendor];
14587
+ if (vendorOverride !== void 0) profile = mergeProfile(profile, vendorOverride);
14588
+ const familyOverride = FAMILY_PROFILES.find(
14589
+ (e) => e.vendor === identity.vendor && e.family === identity.family
14590
+ );
14591
+ if (familyOverride !== void 0) profile = mergeProfile(profile, familyOverride.override);
14592
+ profile = applyQuirkOverlay(profile, identity.quirks);
14593
+ return profile;
14594
+ }
14595
+ function mergeProfile(base, override) {
14596
+ return {
14597
+ ...base,
14598
+ ...override
14599
+ };
14600
+ }
14601
+ function applyQuirkOverlay(profile, identityQuirks) {
14602
+ if (identityQuirks.length === 0) return profile;
14603
+ const merged = /* @__PURE__ */ new Set([...profile.quirks, ...identityQuirks]);
14604
+ let maxBudget = profile.maxRecommendedTurnBudget;
14605
+ if (identityQuirks.includes("thinking")) {
14606
+ maxBudget = Math.ceil(maxBudget * 1.5);
14607
+ }
14608
+ return {
14609
+ ...profile,
14610
+ quirks: [...merged],
14611
+ maxRecommendedTurnBudget: maxBudget
14612
+ };
14613
+ }
14614
+
14615
+ // src/agents/agentic/types.ts
14616
+ var AgentError9 = class extends Error {
14617
+ causeData;
14618
+ constructor(message, cause) {
14619
+ super(message);
14620
+ this.name = "AgentError";
14621
+ if (cause !== void 0) this.causeData = cause;
14622
+ }
14623
+ };
14624
+
14625
+ // src/agents/agentic/agentic-adapter.ts
14626
+ var AgenticAdapter = class {
14627
+ providerId;
14628
+ modelId;
14629
+ /**
14630
+ * Adapter strategy stamp — composed from the resolved model
14631
+ * identity, NOT the IModelAdapter's providerId. For a custom OpenAI
14632
+ * gateway fronting Claude, this reads `native:anthropic` even though
14633
+ * `IModelAdapter.providerId === 'openai'`.
14634
+ *
14635
+ * Initialised eagerly from the modelId parse (sync); upgraded after
14636
+ * the first `runAgent` if the probe contributes a higher-confidence
14637
+ * vendor signal.
14638
+ */
14639
+ adapterStrategy;
14640
+ model;
14641
+ options;
14642
+ semaphore;
14643
+ resolvedIdentity;
14644
+ profile;
14645
+ profileResolutionPromise = null;
14646
+ constructor(modelAdapter, options = {}) {
14647
+ this.model = modelAdapter;
14648
+ this.options = options;
14649
+ this.providerId = modelAdapter.providerId;
14650
+ this.modelId = modelAdapter.modelId;
14651
+ this.resolvedIdentity = resolveModelIdentitySync(modelAdapter.modelId, options.modelHints);
14652
+ this.profile = options.forceProfile ?? lookupModelProfile(this.resolvedIdentity);
14653
+ this.adapterStrategy = stampStrategy(this.resolvedIdentity);
14654
+ if (this.profile.quirks.includes("embedding")) {
14655
+ throw new AgentError9(
14656
+ `Refusing to construct AgenticAdapter for an embedding model (${modelAdapter.modelId}). Agentic loops require a chat-completion model.`
14657
+ );
14658
+ }
14659
+ this.semaphore = options.maxConcurrent !== void 0 && options.maxConcurrent > 0 ? new Semaphore(options.maxConcurrent) : null;
14660
+ }
14661
+ /**
14662
+ * Read-only accessor for the resolved profile. Mostly used in tests
14663
+ * + observability surfaces; production callers shouldn't need this.
14664
+ */
14665
+ getProfile() {
14666
+ return this.profile;
14667
+ }
14668
+ /**
14669
+ * Read-only accessor for the resolved identity. After the first
14670
+ * `runAgent` call (if a probe ran), this may differ from what the
14671
+ * sync constructor stored — useful for audit logs.
14672
+ */
14673
+ getResolvedIdentity() {
14674
+ return this.resolvedIdentity;
14675
+ }
14676
+ /**
14677
+ * Lazily resolve identity via the async probe + refresh the profile.
14678
+ * Idempotent + concurrent-call-safe (multiple `runAgent`s share the
14679
+ * same in-flight resolution).
14680
+ */
14681
+ async ensureIdentityResolved() {
14682
+ if (this.options.forceProfile !== void 0) return;
14683
+ if (this.options.skipProbe === true) return;
14684
+ if (typeof this.model.listModels !== "function") return;
14685
+ if (this.profileResolutionPromise !== null) {
14686
+ await this.profileResolutionPromise;
14687
+ return;
14688
+ }
14689
+ this.profileResolutionPromise = this.doResolveIdentity();
14690
+ try {
14691
+ await this.profileResolutionPromise;
14692
+ } finally {
14693
+ }
14694
+ }
14695
+ /**
14696
+ * If the resolved profile asks for `'ephemeral'` prompt caching,
14697
+ * mark the LAST tool definition with `cache_control: { type:
14698
+ * 'ephemeral' }`. Anthropic interprets this as "cache everything up
14699
+ * to and including the tool definitions"; other providers strip the
14700
+ * unknown field at the canonical-request mapper.
14701
+ *
14702
+ * Last-tool placement is the Anthropic convention: cache key is the
14703
+ * prefix, so caching the final tool block caches the entire tools
14704
+ * section. Per-turn the system prompt + user prompt + tools are
14705
+ * stable, so cache-hit rate on multi-turn agent runs is high.
14706
+ */
14707
+ applyCacheControlIfRequested(tools) {
14708
+ if (this.profile.promptCaching !== "ephemeral") return [...tools];
14709
+ if (tools.length === 0) return [];
14710
+ return tools.map(
14711
+ (tool, idx) => idx === tools.length - 1 ? {
14712
+ ...tool,
14713
+ cacheControl: { type: "ephemeral" }
14714
+ } : tool
14715
+ );
14716
+ }
14717
+ async doResolveIdentity() {
14718
+ const refined = await resolveModelIdentity(this.model, {
14719
+ ...this.options.modelHints !== void 0 && { hints: this.options.modelHints }
14720
+ });
14721
+ if (refined.source === "probe" || refined.source === "modelHints") {
14722
+ this.resolvedIdentity = refined;
14723
+ this.profile = lookupModelProfile(refined);
14724
+ this.adapterStrategy = stampStrategy(refined);
14725
+ }
14726
+ }
14727
+ async runAgent(args) {
14728
+ await this.ensureIdentityResolved();
14729
+ const turnBudget = args.turnBudget ?? this.profile.maxRecommendedTurnBudget;
14730
+ if (turnBudget <= 0) {
14731
+ return err(new AgentError9(`turnBudget must be > 0, got ${String(turnBudget)}`));
14732
+ }
14733
+ const resolvedArgs = { ...args, turnBudget };
14734
+ const state = {
14735
+ turns: [],
14736
+ totalInputTokens: 0,
14737
+ totalOutputTokens: 0,
14738
+ finalContent: "",
14739
+ messages: [{ role: "user", content: args.userPrompt }]
14740
+ };
14741
+ while (state.turns.length < turnBudget) {
14742
+ if (args.signal?.aborted === true) {
14743
+ return ok(this.buildFromState(state, "cancelled"));
14744
+ }
14745
+ const turnOutcome = await this.runOneTurn(resolvedArgs, state);
14746
+ if (turnOutcome.kind === "error") return err(turnOutcome.error);
14747
+ if (turnOutcome.kind === "stop") {
14748
+ return ok(this.buildFromState(state, turnOutcome.reason));
14749
+ }
14750
+ }
14751
+ return ok(this.buildFromState(state, "turn-budget"));
14752
+ }
14753
+ /**
14754
+ * Run one model-call cycle: call → check for tool_use → execute tool
14755
+ * calls → append results. Returns one of three outcomes describing
14756
+ * whether the loop continues, stops naturally, or hit a model error.
14757
+ */
14758
+ async runOneTurn(args, state) {
14759
+ const t0 = Date.now();
14760
+ const completion = await this.callModelGated({
14761
+ messages: state.messages,
14762
+ systemPrompt: args.systemPrompt,
14763
+ tools: this.applyCacheControlIfRequested([...args.tools]),
14764
+ ...args.temperature !== void 0 && { temperature: args.temperature },
14765
+ ...args.maxTokens !== void 0 && { maxTokens: args.maxTokens }
14766
+ });
14767
+ const modelLatencyMs = Date.now() - t0;
14768
+ if (!completion.ok) {
14769
+ return {
14770
+ kind: "error",
14771
+ error: new AgentError9(
14772
+ `Model call failed at turn ${String(state.turns.length)}: ${completion.error.message}`,
14773
+ completion.error
14774
+ )
14775
+ };
14776
+ }
14777
+ const response = completion.value;
14778
+ state.totalInputTokens += response.usage.inputTokens;
14779
+ state.totalOutputTokens += response.usage.outputTokens;
14780
+ const toolUses = response.content.filter(
14781
+ (c) => c.type === "tool_use"
14782
+ );
14783
+ if (toolUses.length === 0) {
14784
+ state.finalContent = extractFinalText(response.content);
14785
+ return { kind: "stop", reason: "agent-stopped" };
14786
+ }
14787
+ state.messages.push({ role: "assistant", content: response.content });
14788
+ return this.processToolCalls(args, state, toolUses, response, modelLatencyMs);
14789
+ }
14790
+ async processToolCalls(args, state, toolUses, response, modelLatencyMs) {
14791
+ if (this.profile.parallelToolCalls && toolUses.length > 1) {
14792
+ return this.processToolCallsParallel(args, state, toolUses, response, modelLatencyMs);
14793
+ }
14794
+ return this.processToolCallsSequential(args, state, toolUses, response, modelLatencyMs);
14795
+ }
14796
+ async processToolCallsSequential(args, state, toolUses, response, modelLatencyMs) {
14797
+ const toolResultBlocks = [];
14798
+ for (const toolUse of toolUses) {
14799
+ if (state.turns.length >= args.turnBudget) break;
14800
+ const outcome = await this.invokeToolAndRecord(
14801
+ args,
14802
+ state,
14803
+ toolUse,
14804
+ response,
14805
+ modelLatencyMs
14806
+ );
14807
+ if (outcome.kind === "stop") return outcome;
14808
+ toolResultBlocks.push(outcome.toolResultBlock);
14809
+ }
14810
+ if (toolResultBlocks.length > 0) {
14811
+ state.messages.push({ role: "user", content: toolResultBlocks });
14812
+ }
14813
+ return { kind: "continue" };
14814
+ }
14815
+ /**
14816
+ * Profile-driven parallel tool execution. Used when
14817
+ * `profile.parallelToolCalls === true` AND the model emitted >1
14818
+ * tool_use block in a single turn.
14819
+ *
14820
+ * Each tool call still produces its own `AgentTurn`; turn-budget
14821
+ * applies to the post-completion count (we don't pre-cap the
14822
+ * Promise.all because the model already issued all calls — better
14823
+ * to record everything and let the next turn be the budget breaker).
14824
+ */
14825
+ async processToolCallsParallel(args, state, toolUses, response, modelLatencyMs) {
14826
+ const startIndex = state.turns.length;
14827
+ const promises2 = toolUses.map(
14828
+ (toolUse, offset) => this.invokeToolForParallel(args, toolUse, response, modelLatencyMs, startIndex + offset)
14829
+ );
14830
+ const outcomes = await Promise.all(promises2);
14831
+ const toolResultBlocks = [];
14832
+ for (const outcome of outcomes) {
14833
+ if (outcome.kind === "stop-tool-error") {
14834
+ state.turns.push(outcome.turn);
14835
+ args.onTurn?.(outcome.turn);
14836
+ return { kind: "stop", reason: "tool-error" };
14837
+ }
14838
+ state.turns.push(outcome.turn);
14839
+ args.onTurn?.(outcome.turn);
14840
+ toolResultBlocks.push(outcome.toolResultBlock);
14841
+ }
14842
+ if (toolResultBlocks.length > 0) {
14843
+ state.messages.push({ role: "user", content: toolResultBlocks });
14844
+ }
14845
+ return { kind: "continue" };
14846
+ }
14847
+ /**
14848
+ * Per-call wrapper used by parallel execution. Returns a captured
14849
+ * outcome (turn + result-block, OR turn + tool-error flag) without
14850
+ * touching shared state — `processToolCallsParallel` reduces the
14851
+ * outcomes deterministically afterwards.
14852
+ */
14853
+ async invokeToolForParallel(args, toolUse, response, modelLatencyMs, turnIndex) {
14854
+ const toolCall = {
14855
+ id: toolUse.id,
14856
+ name: toolUse.name,
14857
+ arguments: toolUse.input
14858
+ };
14859
+ const tt0 = Date.now();
14860
+ try {
14861
+ const toolResult = await args.onToolCall(toolCall);
14862
+ const turn = buildTurn({
14863
+ turnIndex,
14864
+ toolCall,
14865
+ toolResult,
14866
+ modelLatencyMs,
14867
+ toolLatencyMs: Date.now() - tt0,
14868
+ response
14869
+ });
14870
+ return {
14871
+ kind: "recorded",
14872
+ turn,
14873
+ toolResultBlock: {
14874
+ type: "tool_result",
14875
+ tool_use_id: toolUse.id,
14876
+ content: toolResult.content,
14877
+ ...toolResult.isError === true && { is_error: true }
14878
+ }
14879
+ };
14880
+ } catch (caught) {
14881
+ const turn = buildTurn({
14882
+ turnIndex,
14883
+ toolCall,
14884
+ toolResult: {
14885
+ content: caught instanceof Error ? caught.message : String(caught),
14886
+ isError: true
14887
+ },
14888
+ modelLatencyMs,
14889
+ toolLatencyMs: Date.now() - tt0,
14890
+ response
14891
+ });
14892
+ return { kind: "stop-tool-error", turn };
14893
+ }
14894
+ }
14895
+ async invokeToolAndRecord(args, state, toolUse, response, modelLatencyMs) {
14896
+ const toolCall = {
14897
+ id: toolUse.id,
14898
+ name: toolUse.name,
14899
+ arguments: toolUse.input
14900
+ };
14901
+ const tt0 = Date.now();
14902
+ try {
14903
+ const toolResult = await args.onToolCall(toolCall);
14904
+ return recordToolSuccess({
14905
+ args,
14906
+ state,
14907
+ toolUse,
14908
+ toolCall,
14909
+ toolResult,
14910
+ modelLatencyMs,
14911
+ toolLatencyMs: Date.now() - tt0,
14912
+ response
14913
+ });
14914
+ } catch (caught) {
14915
+ const errTurn = buildTurn({
14916
+ turnIndex: state.turns.length,
14917
+ toolCall,
14918
+ toolResult: {
14919
+ content: caught instanceof Error ? caught.message : String(caught),
14920
+ isError: true
14921
+ },
14922
+ modelLatencyMs,
14923
+ toolLatencyMs: Date.now() - tt0,
14924
+ response
14925
+ });
14926
+ state.turns.push(errTurn);
14927
+ args.onTurn?.(errTurn);
14928
+ return { kind: "stop", reason: "tool-error" };
14929
+ }
14930
+ }
14931
+ buildFromState(state, stopReason) {
14932
+ return this.buildResult(
14933
+ state.turns,
14934
+ state.totalInputTokens,
14935
+ state.totalOutputTokens,
14936
+ stopReason,
14937
+ state.finalContent
14938
+ );
14939
+ }
14940
+ /**
14941
+ * Wrap `model.complete` in an optional concurrency gate. The
14942
+ * semaphore is held only across the model API call — released
14943
+ * before tool execution so harnesses doing slow tool calls don't
14944
+ * starve other concurrent `runAgent` calls.
14945
+ */
14946
+ async callModelGated(request) {
14947
+ if (this.semaphore === null) {
14948
+ return this.model.complete(request);
14949
+ }
14950
+ await this.semaphore.acquire();
14951
+ try {
14952
+ return await this.model.complete(request);
14953
+ } finally {
14954
+ this.semaphore.release();
14955
+ }
14956
+ }
14957
+ buildResult(turns, totalInputTokens, totalOutputTokens, stopReason, finalContent) {
14958
+ return {
14959
+ turnsUsed: turns.length,
14960
+ stopReason,
14961
+ turns,
14962
+ ...totalInputTokens > 0 && { totalInputTokens },
14963
+ ...totalOutputTokens > 0 && { totalOutputTokens },
14964
+ providerId: this.providerId,
14965
+ modelId: this.modelId,
14966
+ adapterStrategy: this.adapterStrategy,
14967
+ finalContent
14968
+ };
14969
+ }
14970
+ };
14971
+ function stampStrategy(identity) {
14972
+ return identity.vendor === "unknown" ? "wrapper" : `native:${identity.vendor}`;
14973
+ }
14974
+ function recordToolSuccess(p) {
14975
+ const turn = buildTurn({
14976
+ turnIndex: p.state.turns.length,
14977
+ toolCall: p.toolCall,
14978
+ toolResult: p.toolResult,
14979
+ modelLatencyMs: p.modelLatencyMs,
14980
+ toolLatencyMs: p.toolLatencyMs,
14981
+ response: p.response
14982
+ });
14983
+ p.state.turns.push(turn);
14984
+ p.args.onTurn?.(turn);
14985
+ return {
14986
+ kind: "recorded",
14987
+ toolResultBlock: {
14988
+ type: "tool_result",
14989
+ tool_use_id: p.toolUse.id,
14990
+ content: p.toolResult.content,
14991
+ ...p.toolResult.isError === true && { is_error: true }
14992
+ }
14993
+ };
14994
+ }
14995
+ function buildTurn(b) {
14996
+ return {
14997
+ turnIndex: b.turnIndex,
14998
+ toolCall: b.toolCall,
14999
+ toolResult: b.toolResult,
15000
+ modelLatencyMs: b.modelLatencyMs,
15001
+ toolLatencyMs: b.toolLatencyMs,
15002
+ inputTokens: b.response.usage.inputTokens,
15003
+ outputTokens: b.response.usage.outputTokens
15004
+ };
15005
+ }
15006
+ function extractFinalText(content) {
15007
+ return content.filter((c) => c.type === "text").map((c) => c.text).join("\n").trim();
15008
+ }
15009
+ var Semaphore = class {
15010
+ waiters = [];
15011
+ available;
15012
+ constructor(capacity) {
15013
+ this.available = capacity;
15014
+ }
15015
+ acquire() {
15016
+ if (this.available > 0) {
15017
+ this.available -= 1;
15018
+ return Promise.resolve();
15019
+ }
15020
+ return new Promise((resolve19) => {
15021
+ this.waiters.push(resolve19);
15022
+ });
15023
+ }
15024
+ release() {
15025
+ const next = this.waiters.shift();
15026
+ if (next !== void 0) {
15027
+ next();
15028
+ return;
15029
+ }
15030
+ this.available += 1;
15031
+ }
15032
+ };
15033
+
15034
+ // src/agents/agentic/factory.ts
15035
+ function createAgenticAdapter(modelAdapter, options = {}) {
15036
+ return new AgenticAdapter(modelAdapter, options);
15037
+ }
15038
+
14312
15039
  // src/agents/reasoning/forest-node-types.ts
14313
15040
  import { z as z20 } from "zod";
14314
15041
  var NodeStateSchema = z20.enum(["pending", "active", "completed", "pruned", "error"]);
@@ -49406,6 +50133,8 @@ export {
49406
50133
  SkillLoader,
49407
50134
  createSkillLoader,
49408
50135
  loadAllExternalPacks,
50136
+ AgenticAdapter,
50137
+ createAgenticAdapter,
49409
50138
  NodeStateSchema,
49410
50139
  ReasoningStepTypeSchema,
49411
50140
  ReasoningNodeMetadataSchema,
@@ -49816,4 +50545,4 @@ export {
49816
50545
  detectBackend,
49817
50546
  createTaskTracker
49818
50547
  };
49819
- //# sourceMappingURL=chunk-3HR6UJ2E.js.map
50548
+ //# sourceMappingURL=chunk-J4VR2WNI.js.map