@byfriends/sdk 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -393,6 +393,7 @@ export declare type BackgroundConfig = z.infer<typeof BackgroundConfigSchema>;
393
393
 
394
394
  declare const BackgroundConfigSchema: z.ZodObject<{
395
395
  maxRunningTasks: z.ZodOptional<z.ZodNumber>;
396
+ maxConcurrentSubagents: z.ZodOptional<z.ZodNumber>;
396
397
  keepAliveOnExit: z.ZodOptional<z.ZodBoolean>;
397
398
  killGracePeriodMs: z.ZodOptional<z.ZodNumber>;
398
399
  agentTaskTimeoutS: z.ZodOptional<z.ZodNumber>;
@@ -1217,6 +1218,7 @@ declare const ByfConfigPatchSchema: z.ZodObject<{
1217
1218
  }, z.core.$strip>>;
1218
1219
  background: z.ZodOptional<z.ZodObject<{
1219
1220
  maxRunningTasks: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
1221
+ maxConcurrentSubagents: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
1220
1222
  keepAliveOnExit: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
1221
1223
  killGracePeriodMs: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
1222
1224
  agentTaskTimeoutS: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
@@ -1354,6 +1356,7 @@ declare const ByfConfigSchema: z.ZodObject<{
1354
1356
  }, z.core.$strip>>;
1355
1357
  background: z.ZodOptional<z.ZodObject<{
1356
1358
  maxRunningTasks: z.ZodOptional<z.ZodNumber>;
1359
+ maxConcurrentSubagents: z.ZodOptional<z.ZodNumber>;
1357
1360
  keepAliveOnExit: z.ZodOptional<z.ZodBoolean>;
1358
1361
  killGracePeriodMs: z.ZodOptional<z.ZodNumber>;
1359
1362
  agentTaskTimeoutS: z.ZodOptional<z.ZodNumber>;
@@ -4384,10 +4387,13 @@ declare class SessionSubagentHost {
4384
4387
  private readonly ownerAgentId;
4385
4388
  readonly backgroundTaskTimeoutMs?: number | undefined;
4386
4389
  private readonly activeChildren;
4387
- constructor(session: Session_2, ownerAgentId: string, backgroundTaskTimeoutMs?: number | undefined);
4390
+ /** Maximum concurrent subagents; exposed for testing and config diagnostics. */
4391
+ readonly maxConcurrentSubagents: number;
4392
+ constructor(session: Session_2, ownerAgentId: string, backgroundTaskTimeoutMs?: number | undefined, maxConcurrentSubagents?: number | undefined);
4388
4393
  spawn(profileName: string, options: RunSubagentOptions): Promise<SubagentHandle>;
4389
4394
  resume(agentId: string, options: RunSubagentOptions): Promise<SubagentHandle>;
4390
4395
  cancelAll(): void;
4396
+ private assertCanSpawn;
4391
4397
  getProfileName(agentId: string): string | undefined;
4392
4398
  private resolveProfile;
4393
4399
  private runChild;
package/dist/index.mjs CHANGED
@@ -56166,7 +56166,7 @@ var agent_background_disabled_default = "Background agent execution is disabled
56166
56166
  var agent_background_enabled_default = "When `run_in_background=true`, the subagent runs detached from this turn. The completion arrives in a later turn as a synthetic user-role message containing its result — you do not need to poll, sleep, or check on its progress. Continue with other work or respond to the user. Never fabricate or predict what the result will say.\n\nFor a background task, when `timeout` is omitted it falls back to the operator-configured background timeout, if one is set. If the operator has not configured a background timeout, an omitted `timeout` means the task runs with no time limit.\n";
56167
56167
  //#endregion
56168
56168
  //#region ../agent-core/src/tools/builtin/collaboration/agent.md
56169
- var agent_default = "Launch a subagent to handle a task. The subagent starts with zero context — it has not seen this conversation. Brief it with the goal, what you already know, and exact paths or commands.\n\n- When the task continues earlier work a subagent already did, prefer resuming that agent (pass its `resume` id) over spawning a fresh instance.\n- A subagent's result is only visible to you, not to the user. Summarize the relevant parts yourself when the user needs to see them.\n- Skip delegation for trivial work you can do directly — reading a known file, searching a small set of files, or any one-step task.\n- Once a subagent is running, do not redo its searches or reads in parallel, and do not abandon it midway and finish the job manually.\n";
56169
+ var agent_default = "Launch a subagent to handle a task. The subagent starts with zero context — it has not seen this conversation. Brief it with the goal, what you already know, and exact paths or commands.\n\n- When the task continues earlier work a subagent already did, prefer resuming that agent (pass its `resume` id) over spawning a fresh instance.\n- A subagent's result is only visible to you, not to the user. Summarize the relevant parts yourself when the user needs to see them.\n- Skip delegation for trivial work you can do directly — reading a known file, searching a small set of files, or any one-step task.\n- Once a subagent is running, do not redo its searches or reads in parallel, and do not abandon it midway and finish the job manually.\n- There is a concurrency limit on parallel subagents. If the limit is reached, wait for a running subagent to complete before launching another.\n";
56170
56170
  //#endregion
56171
56171
  //#region ../agent-core/src/tools/builtin/collaboration/agent.ts
56172
56172
  /**
@@ -70841,7 +70841,7 @@ var SkillManager = class {
70841
70841
  const skill = this.registry.getSkill(input.name);
70842
70842
  if (skill === void 0) throw new ByfError(ErrorCodes.SKILL_NOT_FOUND, `Skill "${input.name}" was not found`);
70843
70843
  if (!isUserActivatableSkillType(skill.metadata.type)) throw new ByfError(ErrorCodes.SKILL_TYPE_UNSUPPORTED, `Skill "${skill.name}" cannot be activated by the user`);
70844
- this.recordActivation({
70844
+ const origin = {
70845
70845
  kind: "skill_activation",
70846
70846
  activationId: randomUUID(),
70847
70847
  skillName: skill.name,
@@ -70850,10 +70850,13 @@ var SkillManager = class {
70850
70850
  skillPath: skill.path,
70851
70851
  skillSource: skill.source,
70852
70852
  skillArgs: input.args
70853
- }, [{
70853
+ };
70854
+ const skillContent = this.registry.renderSkillPrompt(skill, input.args ?? "");
70855
+ this.recordActivation(origin, [{
70854
70856
  type: "text",
70855
- text: this.registry.renderSkillPrompt(skill, input.args ?? "")
70857
+ text: skillContent
70856
70858
  }]);
70859
+ this.agent.context.appendSystemReminder(`<byf-skill-loaded name="${skill.name}">\n${skillContent}\n</byf-skill-loaded>`, origin);
70857
70860
  }
70858
70861
  recordActivation(origin, input) {
70859
70862
  this.agent.emitEvent({
@@ -78851,9 +78854,8 @@ function createChatStreamingCallbacks(deps) {
78851
78854
  * enforcement, usage aggregation, optional continuation after non-tool stops,
78852
78855
  * and final `TurnResult` mapping. One-step execution lives in `turn-step.ts`.
78853
78856
  */
78854
- const DEFAULT_MAX_STEPS = 1e3;
78855
78857
  async function runTurn(input) {
78856
- const { turnId, signal, llm, buildMessages, dispatchEvent, tools, hooks, log, maxSteps = DEFAULT_MAX_STEPS, maxRetryAttempts } = input;
78858
+ const { turnId, signal, llm, buildMessages, dispatchEvent, tools, hooks, log, maxSteps, maxRetryAttempts } = input;
78857
78859
  let usage = emptyUsage();
78858
78860
  let steps = 0;
78859
78861
  let stopReason = "end_turn";
@@ -78864,7 +78866,7 @@ async function runTurn(input) {
78864
78866
  try {
78865
78867
  while (true) {
78866
78868
  signal.throwIfAborted();
78867
- if (steps >= maxSteps) throw createMaxStepsExceededError(maxSteps);
78869
+ if (maxSteps !== void 0 && steps >= maxSteps) throw createMaxStepsExceededError(maxSteps);
78868
78870
  steps += 1;
78869
78871
  activeStep = steps;
78870
78872
  const stepResult = await executeLoopStep({
@@ -84517,7 +84519,7 @@ var Agent = class {
84517
84519
  this.usage = new UsageRecorder(this);
84518
84520
  this.tools = new ToolManager(this);
84519
84521
  this.background = new BackgroundManager(this, {
84520
- maxRunningTasks: config.backgroundMaxRunningTasks,
84522
+ maxRunningTasks: config.backgroundMaxRunningTasks ?? 10,
84521
84523
  sessionDir: config.backgroundSessionDir
84522
84524
  });
84523
84525
  this.replayBuilder = new ReplayBuilder(this);
@@ -84889,6 +84891,7 @@ const LoopControlSchema = z.object({
84889
84891
  });
84890
84892
  const BackgroundConfigSchema = z.object({
84891
84893
  maxRunningTasks: z.number().int().min(1).optional(),
84894
+ maxConcurrentSubagents: z.number().int().min(1).optional(),
84892
84895
  keepAliveOnExit: z.boolean().optional(),
84893
84896
  killGracePeriodMs: z.number().int().min(0).optional(),
84894
84897
  agentTaskTimeoutS: z.number().int().min(1).optional(),
@@ -89035,18 +89038,27 @@ const SUMMARY_MAX_LENGTH = 8e3;
89035
89038
  const SUMMARY_CONTINUATION_ATTEMPTS = 1;
89036
89039
  const HOOK_TEXT_PREVIEW_LENGTH = 500;
89037
89040
  const SUBAGENT_MAX_TOKENS_ERROR = "Subagent turn failed before completing its final summary: reason=max_tokens";
89041
+ /**
89042
+ * Maximum number of concurrently running subagents per parent.
89043
+ * Prevents cascading subagent proliferation that exhausts LLM bandwidth.
89044
+ */
89045
+ const DEFAULT_MAX_CONCURRENT_SUBAGENTS = 5;
89038
89046
  var SessionSubagentHost = class {
89039
89047
  session;
89040
89048
  ownerAgentId;
89041
89049
  backgroundTaskTimeoutMs;
89042
89050
  activeChildren = /* @__PURE__ */ new Map();
89043
- constructor(session, ownerAgentId, backgroundTaskTimeoutMs) {
89051
+ /** Maximum concurrent subagents; exposed for testing and config diagnostics. */
89052
+ maxConcurrentSubagents;
89053
+ constructor(session, ownerAgentId, backgroundTaskTimeoutMs, maxConcurrentSubagents = void 0) {
89044
89054
  this.session = session;
89045
89055
  this.ownerAgentId = ownerAgentId;
89046
89056
  this.backgroundTaskTimeoutMs = backgroundTaskTimeoutMs;
89057
+ this.maxConcurrentSubagents = maxConcurrentSubagents ?? DEFAULT_MAX_CONCURRENT_SUBAGENTS;
89047
89058
  }
89048
89059
  async spawn(profileName, options) {
89049
89060
  options.signal.throwIfAborted();
89061
+ this.assertCanSpawn();
89050
89062
  const parent = this.session.agents.get(this.ownerAgentId);
89051
89063
  if (parent === void 0) throw new Error(`Parent agent "${this.ownerAgentId}" was not found`);
89052
89064
  const profile = this.resolveProfile(parent, profileName);
@@ -89076,6 +89088,7 @@ var SessionSubagentHost = class {
89076
89088
  }
89077
89089
  async resume(agentId, options) {
89078
89090
  options.signal.throwIfAborted();
89091
+ this.assertCanSpawn();
89079
89092
  const parent = this.session.agents.get(this.ownerAgentId);
89080
89093
  if (parent === void 0) throw new Error(`Parent agent "${this.ownerAgentId}" was not found`);
89081
89094
  const child = this.session.agents.get(agentId);
@@ -89114,6 +89127,9 @@ var SessionSubagentHost = class {
89114
89127
  child.controller.abort();
89115
89128
  }
89116
89129
  }
89130
+ assertCanSpawn() {
89131
+ if (this.activeChildren.size >= this.maxConcurrentSubagents) throw new Error(`Too many concurrent subagents (${this.activeChildren.size} running, maximum ${this.maxConcurrentSubagents}).`);
89132
+ }
89117
89133
  getProfileName(agentId) {
89118
89134
  const metadata = this.session.metadata.agents[agentId];
89119
89135
  if (metadata?.type !== "sub" || metadata.parentAgentId !== this.ownerAgentId) return;
@@ -89511,7 +89527,7 @@ var Session$1 = class {
89511
89527
  providerManager: this.config.providerManager,
89512
89528
  sessionId: this.config.id,
89513
89529
  hookEngine: config.hookEngine ?? this.hookEngine,
89514
- subagentHost: config.subagentHost ?? new SessionSubagentHost(this, id, this.backgroundTaskTimeoutMs()),
89530
+ subagentHost: config.subagentHost ?? new SessionSubagentHost(this, id, this.backgroundTaskTimeoutMs(), this.config.background?.maxConcurrentSubagents),
89515
89531
  mcp: this.mcp,
89516
89532
  backgroundMaxRunningTasks: this.config.background?.maxRunningTasks,
89517
89533
  backgroundSessionDir: homedir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byfriends/sdk",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Public TypeScript SDK for BYF agents and sessions",
5
5
  "license": "Proprietary",
6
6
  "author": "ByronFinn",
@@ -49,10 +49,10 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/yazl": "^2.4.6",
52
- "@byfriends/kosong": "^0.2.2",
52
+ "@byfriends/agent-core": "^0.2.5",
53
53
  "@byfriends/oauth": "^0.1.1",
54
- "@byfriends/kaos": "^0.2.2",
55
- "@byfriends/agent-core": "^0.2.3"
54
+ "@byfriends/kosong": "^0.2.2",
55
+ "@byfriends/kaos": "^0.2.2"
56
56
  },
57
57
  "scripts": {
58
58
  "build": "tsdown && pnpm run build:dts",