open-multi-agent-kit 0.96.1 → 0.96.2

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.
Files changed (167) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +12 -2
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +1 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/commands/resource-doctor-cli.d.ts +22 -0
  7. package/dist/commands/resource-doctor-cli.d.ts.map +1 -0
  8. package/dist/commands/resource-doctor-cli.js +98 -0
  9. package/dist/commands/resource-doctor-cli.js.map +1 -0
  10. package/dist/core/agent-session.d.ts +37 -1
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +289 -5
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/completion-sound.d.ts +109 -0
  15. package/dist/core/completion-sound.d.ts.map +1 -0
  16. package/dist/core/completion-sound.js +165 -0
  17. package/dist/core/completion-sound.js.map +1 -0
  18. package/dist/core/context-budget-system-prompt-items.d.ts.map +1 -1
  19. package/dist/core/context-budget-system-prompt-items.js +2 -2
  20. package/dist/core/context-budget-system-prompt-items.js.map +1 -1
  21. package/dist/core/host-resource-probe.d.ts +63 -0
  22. package/dist/core/host-resource-probe.d.ts.map +1 -0
  23. package/dist/core/host-resource-probe.js +126 -0
  24. package/dist/core/host-resource-probe.js.map +1 -0
  25. package/dist/core/host-resource-snapshot.d.ts +54 -0
  26. package/dist/core/host-resource-snapshot.d.ts.map +1 -0
  27. package/dist/core/host-resource-snapshot.js +139 -0
  28. package/dist/core/host-resource-snapshot.js.map +1 -0
  29. package/dist/core/prompt-settlement.d.ts +81 -0
  30. package/dist/core/prompt-settlement.d.ts.map +1 -0
  31. package/dist/core/prompt-settlement.js +84 -0
  32. package/dist/core/prompt-settlement.js.map +1 -0
  33. package/dist/core/provider-resilience.d.ts.map +1 -1
  34. package/dist/core/provider-resilience.js +1 -1
  35. package/dist/core/provider-resilience.js.map +1 -1
  36. package/dist/core/provider-retry.d.ts +7 -0
  37. package/dist/core/provider-retry.d.ts.map +1 -1
  38. package/dist/core/provider-retry.js +24 -0
  39. package/dist/core/provider-retry.js.map +1 -1
  40. package/dist/core/provider-usage.d.ts +15 -2
  41. package/dist/core/provider-usage.d.ts.map +1 -1
  42. package/dist/core/provider-usage.js +110 -2
  43. package/dist/core/provider-usage.js.map +1 -1
  44. package/dist/core/reasoning-router-resolver.d.ts.map +1 -1
  45. package/dist/core/reasoning-router-resolver.js +1 -1
  46. package/dist/core/reasoning-router-resolver.js.map +1 -1
  47. package/dist/core/resource-admission-config.d.ts +55 -0
  48. package/dist/core/resource-admission-config.d.ts.map +1 -0
  49. package/dist/core/resource-admission-config.js +97 -0
  50. package/dist/core/resource-admission-config.js.map +1 -0
  51. package/dist/core/resource-admission.d.ts +81 -0
  52. package/dist/core/resource-admission.d.ts.map +1 -0
  53. package/dist/core/resource-admission.js +128 -0
  54. package/dist/core/resource-admission.js.map +1 -0
  55. package/dist/core/resource-governor-format.d.ts +50 -0
  56. package/dist/core/resource-governor-format.d.ts.map +1 -0
  57. package/dist/core/resource-governor-format.js +113 -0
  58. package/dist/core/resource-governor-format.js.map +1 -0
  59. package/dist/core/resource-governor-settings.d.ts +56 -0
  60. package/dist/core/resource-governor-settings.d.ts.map +1 -0
  61. package/dist/core/resource-governor-settings.js +89 -0
  62. package/dist/core/resource-governor-settings.js.map +1 -0
  63. package/dist/core/resource-observation-journal.d.ts +74 -0
  64. package/dist/core/resource-observation-journal.d.ts.map +1 -0
  65. package/dist/core/resource-observation-journal.js +198 -0
  66. package/dist/core/resource-observation-journal.js.map +1 -0
  67. package/dist/core/resource-safety-gate.d.ts +54 -0
  68. package/dist/core/resource-safety-gate.d.ts.map +1 -0
  69. package/dist/core/resource-safety-gate.js +51 -0
  70. package/dist/core/resource-safety-gate.js.map +1 -0
  71. package/dist/core/run-journal.d.ts.map +1 -1
  72. package/dist/core/run-journal.js +13 -1
  73. package/dist/core/run-journal.js.map +1 -1
  74. package/dist/core/run-resource-lease.d.ts +73 -0
  75. package/dist/core/run-resource-lease.d.ts.map +1 -0
  76. package/dist/core/run-resource-lease.js +78 -0
  77. package/dist/core/run-resource-lease.js.map +1 -0
  78. package/dist/core/session-bash-service.d.ts +12 -0
  79. package/dist/core/session-bash-service.d.ts.map +1 -1
  80. package/dist/core/session-bash-service.js +14 -0
  81. package/dist/core/session-bash-service.js.map +1 -1
  82. package/dist/core/session-system-prompt.d.ts +2 -0
  83. package/dist/core/session-system-prompt.d.ts.map +1 -1
  84. package/dist/core/session-system-prompt.js +2 -0
  85. package/dist/core/session-system-prompt.js.map +1 -1
  86. package/dist/core/session-termination.d.ts +7 -2
  87. package/dist/core/session-termination.d.ts.map +1 -1
  88. package/dist/core/session-termination.js +29 -1
  89. package/dist/core/session-termination.js.map +1 -1
  90. package/dist/core/settings-manager.d.ts +12 -0
  91. package/dist/core/settings-manager.d.ts.map +1 -1
  92. package/dist/core/settings-manager.js +36 -0
  93. package/dist/core/settings-manager.js.map +1 -1
  94. package/dist/core/slash-commands.d.ts.map +1 -1
  95. package/dist/core/slash-commands.js +1 -0
  96. package/dist/core/slash-commands.js.map +1 -1
  97. package/dist/core/subagent-lane-launcher.d.ts +64 -0
  98. package/dist/core/subagent-lane-launcher.d.ts.map +1 -0
  99. package/dist/core/subagent-lane-launcher.js +107 -0
  100. package/dist/core/subagent-lane-launcher.js.map +1 -0
  101. package/dist/core/system-cpu-sampler.d.ts +45 -0
  102. package/dist/core/system-cpu-sampler.d.ts.map +1 -0
  103. package/dist/core/system-cpu-sampler.js +95 -0
  104. package/dist/core/system-cpu-sampler.js.map +1 -0
  105. package/dist/core/system-prompt.d.ts.map +1 -1
  106. package/dist/core/system-prompt.js +5 -8
  107. package/dist/core/system-prompt.js.map +1 -1
  108. package/dist/core/workload-classifier.d.ts +32 -0
  109. package/dist/core/workload-classifier.d.ts.map +1 -0
  110. package/dist/core/workload-classifier.js +66 -0
  111. package/dist/core/workload-classifier.js.map +1 -0
  112. package/dist/core/workload-family-matcher.d.ts +3 -0
  113. package/dist/core/workload-family-matcher.d.ts.map +1 -0
  114. package/dist/core/workload-family-matcher.js +126 -0
  115. package/dist/core/workload-family-matcher.js.map +1 -0
  116. package/dist/core/workload-permit-pool.d.ts +78 -0
  117. package/dist/core/workload-permit-pool.d.ts.map +1 -0
  118. package/dist/core/workload-permit-pool.js +138 -0
  119. package/dist/core/workload-permit-pool.js.map +1 -0
  120. package/dist/core/workload-shard-executor.d.ts +68 -0
  121. package/dist/core/workload-shard-executor.d.ts.map +1 -0
  122. package/dist/core/workload-shard-executor.js +156 -0
  123. package/dist/core/workload-shard-executor.js.map +1 -0
  124. package/dist/core/workload-shard-plan.d.ts +81 -0
  125. package/dist/core/workload-shard-plan.d.ts.map +1 -0
  126. package/dist/core/workload-shard-plan.js +183 -0
  127. package/dist/core/workload-shard-plan.js.map +1 -0
  128. package/dist/core/workload-shard-runner.d.ts +11 -0
  129. package/dist/core/workload-shard-runner.d.ts.map +1 -0
  130. package/dist/core/workload-shard-runner.js +98 -0
  131. package/dist/core/workload-shard-runner.js.map +1 -0
  132. package/dist/core/workload-shard-store.d.ts +44 -0
  133. package/dist/core/workload-shard-store.d.ts.map +1 -0
  134. package/dist/core/workload-shard-store.js +189 -0
  135. package/dist/core/workload-shard-store.js.map +1 -0
  136. package/dist/core/workload-sharders.d.ts +47 -0
  137. package/dist/core/workload-sharders.d.ts.map +1 -0
  138. package/dist/core/workload-sharders.js +162 -0
  139. package/dist/core/workload-sharders.js.map +1 -0
  140. package/dist/core/workload-shell-scan.d.ts +12 -0
  141. package/dist/core/workload-shell-scan.d.ts.map +1 -0
  142. package/dist/core/workload-shell-scan.js +79 -0
  143. package/dist/core/workload-shell-scan.js.map +1 -0
  144. package/dist/main.d.ts.map +1 -1
  145. package/dist/main.js +6 -0
  146. package/dist/main.js.map +1 -1
  147. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  148. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  149. package/dist/modes/interactive/interactive-mode.js +81 -0
  150. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  151. package/docs/providers.md +1 -1
  152. package/docs/sdk.md +4 -0
  153. package/docs/settings.md +64 -2
  154. package/docs/skills.md +16 -0
  155. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  156. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  157. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  158. package/examples/extensions/gondolin/package-lock.json +2 -2
  159. package/examples/extensions/gondolin/package.json +1 -1
  160. package/examples/extensions/sandbox/package-lock.json +2 -2
  161. package/examples/extensions/sandbox/package.json +1 -1
  162. package/examples/extensions/subagent/agent-capability-router.test.ts +7 -0
  163. package/examples/extensions/subagent/domain-profiles.ts +0 -1
  164. package/examples/extensions/with-deps/package-lock.json +2 -2
  165. package/examples/extensions/with-deps/package.json +1 -1
  166. package/npm-shrinkwrap.json +18 -18
  167. package/package.json +7 -9
@@ -44,6 +44,7 @@ import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
44
44
  import { emitSessionShutdownEvent } from "./extensions/runner.js";
45
45
  import { assertTextChatModelForCompletion } from "./grok-harness.js";
46
46
  import { grokPlaybookAppendForProvider } from "./grok-playbook.js";
47
+ import { captureHostResourceSnapshot } from "./host-resource-snapshot.js";
47
48
  import { decideLoadoutAccess } from "./loadout-access-policy.js";
48
49
  import { loadMcpServerConfigs } from "./mcp/config.js";
49
50
  import { McpManager } from "./mcp/manager.js";
@@ -53,6 +54,7 @@ import { TurnMetricsSink } from "./turn-metrics.js";
53
54
  const DISABLED_ENV_VALUES = new Set(["0", "false", "off", "disable", "disabled"]);
54
55
  const MAX_OVERFLOW_RECOVERY_ATTEMPTS = 2;
55
56
  const OVERFLOW_RECOVERY_EMERGENCY_TOKENS = 4_096;
57
+ const MAX_RESOURCE_OBSERVATION_JOURNALS = 32;
56
58
  function isDisabledEnvValue(value) {
57
59
  return value !== undefined && DISABLED_ENV_VALUES.has(value.trim().toLowerCase());
58
60
  }
@@ -75,6 +77,7 @@ import { selectContextFilesForModel } from "./model-prompt-policy.js";
75
77
  import { findExactModelReferenceMatch } from "./model-resolver.js";
76
78
  import { computePromptTokenBudget } from "./prompt-budget.js";
77
79
  import { classifyPromptCacheTransition } from "./prompt-cache.js";
80
+ import { createPromptSettlementState, reducePromptSettlement, settlePromptIfReady, } from "./prompt-settlement.js";
78
81
  import { expandPromptTemplate } from "./prompt-templates.js";
79
82
  import { isQuotaExhaustionMessage, isStickySafetyModel, pickFailoverCandidate, resolveProviderResilience, shouldEjectStickySafetyModel, shouldHonorSafetyFailover, stickySafetyBlockMessage, } from "./provider-resilience.js";
80
83
  import { computeRetryDelayMs, failoverModelKey, isFailoverTriggerError, isRetryableAssistantError, nextRetryAttempt, retryBudgetForAssistantError, } from "./provider-retry.js";
@@ -82,8 +85,13 @@ import { getBiasStepsForCell, parseRouterBiasSnapshot } from "./reasoning-router
82
85
  import { classifyTaskV4, deriveRouterFeedbackFeaturesV4, resolveThinkingLevelV4WithUncertainty, TASK_CLASS_THINKING_LEVELS_V4, } from "./reasoning-router-v4.js";
83
86
  import { redactSensitiveText, redactSensitiveTextForced } from "./redaction.js";
84
87
  import { getRepositoryRouterLearningPaths } from "./repository-learning-scope.js";
88
+ import { decideResourceAdmission } from "./resource-admission.js";
89
+ import { resolveResourceGovernorSettings } from "./resource-governor-settings.js";
90
+ import { admissionObservationFacts, classificationObservationFacts, ResourceObservationJournal, settledObservationFacts, snapshotObservationFacts, soundObservationFacts, } from "./resource-observation-journal.js";
91
+ import { decideResourceSafetyGate, RESOURCE_PRESSURE_REQUIRED_ACTION } from "./resource-safety-gate.js";
85
92
  import { appendRouterFeedbackRecord, ROUTER_FEEDBACK_LEVELS, } from "./router-feedback-collector.js";
86
93
  import { RunJournalStore } from "./run-journal-store.js";
94
+ import { RunResourceLeaseController } from "./run-resource-lease.js";
87
95
  import { SessionBashRuntime } from "./session-bash-runtime.js";
88
96
  import { SessionBashService } from "./session-bash-service.js";
89
97
  import { SessionCompactionService } from "./session-compaction-service.js";
@@ -98,6 +106,8 @@ import { createAllToolDefinitions } from "./tools/index.js";
98
106
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
99
107
  import { createVerifiedBashOperations } from "./verified-bash-adapter.js";
100
108
  import { resolveSessionWorkspaceScope } from "./verified-bash-runtime.js";
109
+ import { classifyWorkloadCommand } from "./workload-classifier.js";
110
+ import { WorkloadPermitError, WorkloadPermitPool } from "./workload-permit-pool.js";
101
111
  /**
102
112
  * Parse a skill block from message text.
103
113
  * Returns null if the text doesn't contain a skill block.
@@ -356,6 +366,14 @@ export class AgentSession {
356
366
  _runJournalStore;
357
367
  _ownedSessionOwnerLease;
358
368
  _activeRunId = null;
369
+ // Resource governor (roadmap M2): lease controller owns the temporary tool
370
+ // cap for governed runs; the last decision backs the §19.4 read-only API.
371
+ _resourceLeaseController;
372
+ _resourceObservations = null;
373
+ _resourceObservationJournals = new Map();
374
+ _latestResourcePromptRunId = null;
375
+ _lastResourceAdmission = null;
376
+ _workloadPermitPool;
359
377
  _pendingRuntimeTerminationCause;
360
378
  _activeRunToolTermination;
361
379
  _lastTermination;
@@ -436,6 +454,7 @@ export class AgentSession {
436
454
  isStreaming: () => this.isStreaming,
437
455
  pushMessage: (message) => this.agent.state.messages.push(message),
438
456
  appendMessage: (message) => this.sessionManager.appendMessage(message),
457
+ acquireResourcePermit: (command, signal) => this._acquireBashResourcePermit(command, signal),
439
458
  });
440
459
  this._compactionService = new SessionCompactionService({
441
460
  sessionManager: this.sessionManager,
@@ -1627,7 +1646,15 @@ export class AgentSession {
1627
1646
  this._promptCacheKey = nextKey;
1628
1647
  this._promptCacheStablePrefixCharacters = boundary ?? 0;
1629
1648
  }
1649
+ _getDefaultActiveSkills() {
1650
+ const trustedSkillNames = new Set(this._resourceLoader
1651
+ .getSkills()
1652
+ .skills.filter((skill) => skill.sourceInfo.scope === "user")
1653
+ .map((skill) => skill.name));
1654
+ return this.settingsManager.getDefaultActiveSkills().filter((name) => trustedSkillNames.has(name));
1655
+ }
1630
1656
  _rebuildSystemPrompt(toolNames, model = this.model) {
1657
+ const defaultActiveSkills = this._getDefaultActiveSkills();
1631
1658
  const assembled = assembleSessionSystemPrompt({
1632
1659
  cwd: this._cwd,
1633
1660
  toolNames,
@@ -1638,6 +1665,8 @@ export class AgentSession {
1638
1665
  appendSystemPrompt: this._resourceLoader.getAppendSystemPrompt(),
1639
1666
  providerAppend: grokPlaybookAppendForProvider(model?.provider),
1640
1667
  skills: this._resourceLoader.getSkills().skills,
1668
+ activeSkillNames: defaultActiveSkills,
1669
+ activeSkillSource: defaultActiveSkills.length > 0 ? "settings" : undefined,
1641
1670
  contextFiles: selectContextFilesForModel(this._resourceLoader.getAgentsFiles().agentsFiles, model),
1642
1671
  contextBudget: this._getContextBudgetOptions(undefined, model),
1643
1672
  });
@@ -1649,6 +1678,15 @@ export class AgentSession {
1649
1678
  // Prompting
1650
1679
  // =========================================================================
1651
1680
  async _runAgentPrompt(messages) {
1681
+ // One identity per top-level run (§16.2): internal retries and
1682
+ // continuations inside this call share it, as does the resource lease.
1683
+ const promptRunId = `prompt-run-${randomUUID()}`;
1684
+ const startedAtEpochMs = Date.now();
1685
+ // Roadmap M2: the lease spans the whole run including internal retries
1686
+ // and continuations, so they share one admission decision (§8.3).
1687
+ const resourceLease = await this._beginResourceGovernedRun(promptRunId);
1688
+ const resourceObservations = this._resourceObservationJournals.get(promptRunId) ?? null;
1689
+ let outcome = "completed";
1652
1690
  try {
1653
1691
  await this.agent.prompt(messages);
1654
1692
  while (await this._handlePostAgentRun()) {
@@ -1656,11 +1694,251 @@ export class AgentSession {
1656
1694
  }
1657
1695
  }
1658
1696
  catch (error) {
1697
+ outcome =
1698
+ this._userAbortRequested || (error instanceof Error && /abort/i.test(error.message)) ? "aborted" : "failed";
1659
1699
  this._publishRuntimeFailure(error);
1660
1700
  throw error;
1661
1701
  }
1662
1702
  finally {
1703
+ if (resourceLease !== null) {
1704
+ // Generation-safe: a stale release after another run acquired the
1705
+ // lease is a no-op instead of clobbering the newer cap (§8.1).
1706
+ this._resourceLeaseController?.release(resourceLease);
1707
+ resourceObservations?.record("resource_lease_released_v1", { promptRunId });
1708
+ }
1663
1709
  this._flushPendingBashMessages();
1710
+ // §16.4 (M4): settle only after every in-run continuation drained.
1711
+ this._emitPromptSettledIfReady({ promptRunId, startedAtEpochMs, outcome, resourceObservations });
1712
+ }
1713
+ }
1714
+ /**
1715
+ * Emit `prompt_settled` when the §16.4 conditions hold. Called exactly
1716
+ * once per promptRunId (from `_runAgentPrompt`'s finally), so the same
1717
+ * run can never emit twice; late-queued messages block emission
1718
+ * conservatively — the continuation run settles under its own id.
1719
+ * Settlement is a UX signal only and must never affect the run (§16.1).
1720
+ */
1721
+ _emitPromptSettledIfReady(input) {
1722
+ try {
1723
+ let state = createPromptSettlementState(input.promptRunId, input.startedAtEpochMs);
1724
+ state = reducePromptSettlement(state, { kind: "terminal", outcome: input.outcome });
1725
+ if (this.isStreaming) {
1726
+ state = reducePromptSettlement(state, { kind: "tool", delta: 1 });
1727
+ }
1728
+ if (this.agent.hasQueuedMessages()) {
1729
+ state = reducePromptSettlement(state, { kind: "continuation", delta: 1 });
1730
+ }
1731
+ const settled = settlePromptIfReady(state, Date.now());
1732
+ if (settled.event !== null) {
1733
+ input.resourceObservations?.record("prompt_settled_v1", settledObservationFacts(settled.event));
1734
+ this._emit(settled.event);
1735
+ }
1736
+ }
1737
+ catch {
1738
+ // Never let settlement bookkeeping break a run.
1739
+ }
1740
+ }
1741
+ /**
1742
+ * Resource governor preflight for one top-level run (roadmap §8, §25.3 M2).
1743
+ *
1744
+ * - `off`: nothing; v0.96.1 observable behavior is preserved.
1745
+ * - `observe` (default): fire-and-forget probe records a decision for the
1746
+ * §19.4 read-only API; the cap never changes and the prompt is never
1747
+ * delayed. The pending probe is bounded (~cpuSampleMs) and never rejects.
1748
+ * - `adaptive`/`strict`: bounded blocking probe (default 300 ms deadline),
1749
+ * then a generation-safe lease applies the effective tool cap for this
1750
+ * run. The caller's `finally` releases it exactly once.
1751
+ *
1752
+ * Never throws: any probe or policy failure leaves this run ungoverned
1753
+ * (§2.1: probe failure must not crash or block the prompt).
1754
+ */
1755
+ async _beginResourceGovernedRun(promptRunId) {
1756
+ try {
1757
+ const resolved = resolveResourceGovernorSettings(this.settingsManager.getResourceGovernorSettings());
1758
+ if (resolved.mode === "off") {
1759
+ return null;
1760
+ }
1761
+ const resourceObservations = this._openResourceObservations(promptRunId);
1762
+ const probeOptions = {
1763
+ cwd: this._cwd,
1764
+ maxProbeMs: resolved.maxProbeMs,
1765
+ cpuSampleMs: resolved.cpuSampleMs,
1766
+ };
1767
+ // §7.3: Agent.maxToolConcurrency uses undefined for "unlimited"; the
1768
+ // admission contract uses 0 for the same meaning.
1769
+ const configuredCaps = { maxToolConcurrency: this.agent.maxToolConcurrency ?? 0 };
1770
+ if (resolved.mode === "observe") {
1771
+ void captureHostResourceSnapshot(probeOptions)
1772
+ .then((snapshot) => {
1773
+ const decision = decideResourceAdmission({
1774
+ snapshot,
1775
+ config: resolved.admission,
1776
+ configuredCaps,
1777
+ });
1778
+ if (this._latestResourcePromptRunId === promptRunId) {
1779
+ this._lastResourceAdmission = decision;
1780
+ }
1781
+ resourceObservations?.record("resource_snapshot_v1", snapshotObservationFacts(snapshot));
1782
+ resourceObservations?.record("resource_admission_v1", admissionObservationFacts(decision));
1783
+ })
1784
+ .catch(() => { });
1785
+ return null;
1786
+ }
1787
+ const snapshot = await captureHostResourceSnapshot(probeOptions);
1788
+ const decision = decideResourceAdmission({ snapshot, config: resolved.admission, configuredCaps });
1789
+ if (this._latestResourcePromptRunId === promptRunId) {
1790
+ this._lastResourceAdmission = decision;
1791
+ }
1792
+ resourceObservations?.record("resource_snapshot_v1", snapshotObservationFacts(snapshot));
1793
+ resourceObservations?.record("resource_admission_v1", admissionObservationFacts(decision));
1794
+ this._resourceLeaseController ??= new RunResourceLeaseController({
1795
+ getCap: () => this.agent.maxToolConcurrency,
1796
+ setCap: (cap) => {
1797
+ this.agent.maxToolConcurrency = cap;
1798
+ },
1799
+ });
1800
+ const lease = this._resourceLeaseController.acquire({ promptRunId, decision });
1801
+ resourceObservations?.record("resource_lease_acquired_v1", {
1802
+ decisionId: decision.decisionId,
1803
+ appliedToolCap: decision.maxToolConcurrency,
1804
+ });
1805
+ return lease;
1806
+ }
1807
+ catch {
1808
+ return null;
1809
+ }
1810
+ }
1811
+ _openResourceObservations(promptRunId) {
1812
+ this._latestResourcePromptRunId = promptRunId;
1813
+ try {
1814
+ const journal = ResourceObservationJournal.open(this._cwd, promptRunId);
1815
+ this._resourceObservations = journal;
1816
+ this._resourceObservationJournals.set(promptRunId, journal);
1817
+ while (this._resourceObservationJournals.size > MAX_RESOURCE_OBSERVATION_JOURNALS) {
1818
+ const oldestPromptRunId = this._resourceObservationJournals.keys().next().value;
1819
+ if (oldestPromptRunId === undefined)
1820
+ break;
1821
+ this._resourceObservationJournals.delete(oldestPromptRunId);
1822
+ }
1823
+ return journal;
1824
+ }
1825
+ catch {
1826
+ this._resourceObservations = null;
1827
+ this._resourceObservationJournals.delete(promptRunId);
1828
+ return null;
1829
+ }
1830
+ }
1831
+ /** §20.2: record the TUI-side completion sound result in its originating run journal. */
1832
+ recordCompletionSoundResult(promptRunId, result) {
1833
+ const journal = this._resourceObservationJournals.get(promptRunId);
1834
+ journal?.record("completion_sound_result_v1", soundObservationFacts(result));
1835
+ this._resourceObservationJournals.delete(promptRunId);
1836
+ if (this._latestResourcePromptRunId === promptRunId) {
1837
+ this._latestResourcePromptRunId = null;
1838
+ if (this._resourceObservations === journal)
1839
+ this._resourceObservations = null;
1840
+ }
1841
+ }
1842
+ /** §19.4 read-only SDK surface: the most recent resource admission decision, if any. */
1843
+ getCurrentResourceAdmission() {
1844
+ return this._lastResourceAdmission;
1845
+ }
1846
+ /** §19.4 read-only SDK surface: capture a fresh host resource snapshot. */
1847
+ async getHostResourceSnapshot() {
1848
+ const resolved = resolveResourceGovernorSettings(this.settingsManager.getResourceGovernorSettings());
1849
+ return captureHostResourceSnapshot({
1850
+ cwd: this._cwd,
1851
+ maxProbeMs: resolved.maxProbeMs,
1852
+ cpuSampleMs: resolved.cpuSampleMs,
1853
+ });
1854
+ }
1855
+ /** §19.4 read-only SDK surface: shared workload permit pool state, if any. */
1856
+ getWorkloadPermitSnapshot() {
1857
+ return this._workloadPermitPool?.snapshot() ?? null;
1858
+ }
1859
+ /**
1860
+ * §14.1 shared-budget seam: child subagents must reuse this instance
1861
+ * instead of creating a private pool (host oversubscription). The live
1862
+ * child launcher wiring lands in M6.
1863
+ */
1864
+ get workloadPermitPool() {
1865
+ this._workloadPermitPool ??= new WorkloadPermitPool();
1866
+ return this._workloadPermitPool;
1867
+ }
1868
+ /**
1869
+ * Resource safety gate for the bash boundary (roadmap §9.4/§11, M3).
1870
+ * Runs only in adaptive/strict mode with a recorded admission decision;
1871
+ * observe/off keep v0.96.1 behavior. Never throws: gate machinery
1872
+ * failures leave the command ungated (§2.1), while computed block
1873
+ * verdicts and permit rejections return a bounded §11.3 payload.
1874
+ */
1875
+ async _acquireBashResourcePermit(command, signal) {
1876
+ try {
1877
+ const resolved = resolveResourceGovernorSettings(this.settingsManager.getResourceGovernorSettings());
1878
+ if (resolved.mode !== "adaptive" && resolved.mode !== "strict") {
1879
+ return {};
1880
+ }
1881
+ const decision = this._lastResourceAdmission;
1882
+ if (decision === null) {
1883
+ return {};
1884
+ }
1885
+ const classification = classifyWorkloadCommand(command);
1886
+ this._resourceObservations?.record("workload_classification_v1", classificationObservationFacts(classification));
1887
+ const verdict = decideResourceSafetyGate({ commandSafety: "allowed", classification, decision });
1888
+ if (verdict.kind === "allow") {
1889
+ return {};
1890
+ }
1891
+ if (verdict.kind === "block") {
1892
+ return { blocked: JSON.stringify(verdict.block) };
1893
+ }
1894
+ this._workloadPermitPool ??= new WorkloadPermitPool({ capacity: decision.maxHeavyProcesses });
1895
+ const pool = this._workloadPermitPool;
1896
+ pool.setCapacity(decision.maxHeavyProcesses);
1897
+ const waitStartedMs = Date.now();
1898
+ this._resourceObservations?.record("workload_permit_wait_v1", {
1899
+ workloadClass: classification.workloadClass,
1900
+ weight: verdict.weight,
1901
+ });
1902
+ try {
1903
+ const permit = await pool.acquire({
1904
+ requestId: `bash-${randomUUID()}`,
1905
+ promptRunId: this._resourceLeaseController?.activeLease?.promptRunId ?? "unleased",
1906
+ workloadClass: classification.workloadClass,
1907
+ weight: verdict.weight,
1908
+ signal,
1909
+ // Bounded §10.3 timeout-aware wait; a settings knob can follow later.
1910
+ timeoutMs: 60_000,
1911
+ });
1912
+ this._resourceObservations?.record("workload_permit_acquired_v1", {
1913
+ workloadClass: classification.workloadClass,
1914
+ weight: verdict.weight,
1915
+ waitMs: Date.now() - waitStartedMs,
1916
+ });
1917
+ return {
1918
+ release: () => {
1919
+ permit.release();
1920
+ this._resourceObservations?.record("workload_permit_released_v1", {
1921
+ workloadClass: classification.workloadClass,
1922
+ weight: verdict.weight,
1923
+ });
1924
+ },
1925
+ };
1926
+ }
1927
+ catch (error) {
1928
+ const code = error instanceof WorkloadPermitError ? error.code : "queue_overflow";
1929
+ return {
1930
+ blocked: JSON.stringify({
1931
+ kind: "resource_pressure",
1932
+ pressure: decision.pressure,
1933
+ action: "defer-heavy",
1934
+ reasonCodes: [...decision.reasons, `resource.permit.${code}`],
1935
+ requiredAction: RESOURCE_PRESSURE_REQUIRED_ACTION,
1936
+ }),
1937
+ };
1938
+ }
1939
+ }
1940
+ catch {
1941
+ return {};
1664
1942
  }
1665
1943
  }
1666
1944
  async _handlePostAgentRun() {
@@ -1702,8 +1980,13 @@ export class AgentSession {
1702
1980
  const expandPromptTemplates = options?.expandPromptTemplates ?? true;
1703
1981
  const preflightResult = options?.preflightResult;
1704
1982
  let currentText = redactSensitiveText(text);
1705
- let promptActiveSkillNames = [...(options?.activeSkillNames ?? [])];
1706
- let promptActiveSkillSource = options?.activeSkillSource;
1983
+ const defaultActiveSkills = this._getDefaultActiveSkills();
1984
+ let promptActiveSkillNames = mergePromptActiveSkillNames(defaultActiveSkills, options?.activeSkillNames ?? []);
1985
+ let promptActiveSkillSource = defaultActiveSkills.length > 0
1986
+ ? options?.activeSkillSource
1987
+ ? `settings+${options.activeSkillSource}`
1988
+ : "settings"
1989
+ : options?.activeSkillSource;
1707
1990
  let isBangSkillInvocation = false;
1708
1991
  if (expandPromptTemplates) {
1709
1992
  const bangInvocation = parseBangInvocation(text, {
@@ -1715,7 +1998,8 @@ export class AgentSession {
1715
1998
  ? `/skill:${bangInvocation.skillName} ${bangInvocation.prompt}`
1716
1999
  : `/skill:${bangInvocation.skillName}`;
1717
2000
  promptActiveSkillNames = mergePromptActiveSkillNames(promptActiveSkillNames, bangInvocation.activeSkillNames);
1718
- promptActiveSkillSource = bangInvocation.source;
2001
+ promptActiveSkillSource =
2002
+ defaultActiveSkills.length > 0 ? `settings+${bangInvocation.source}` : bangInvocation.source;
1719
2003
  }
1720
2004
  }
1721
2005
  let messages;
@@ -1819,8 +2103,8 @@ export class AgentSession {
1819
2103
  const turnSystemPromptOptions = {
1820
2104
  ...this._baseSystemPromptOptions,
1821
2105
  contextBudget: this._getContextBudgetOptions(expandedText),
1822
- ...(promptActiveSkillNames.length > 0 ? { activeSkillNames: promptActiveSkillNames } : {}),
1823
- ...(promptActiveSkillSource ? { activeSkillSource: promptActiveSkillSource } : {}),
2106
+ activeSkillNames: promptActiveSkillNames,
2107
+ activeSkillSource: promptActiveSkillSource,
1824
2108
  };
1825
2109
  const turnSystemPrompt = buildSystemPromptPlan(turnSystemPromptOptions);
1826
2110
  // Emit before_agent_start extension event