usebeeline 0.0.50 → 0.0.52

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 (2) hide show
  1. package/dist/usebeeline.mjs +170 -48
  2. package/package.json +1 -1
@@ -14141,6 +14141,39 @@ import { basename as basename3, dirname as dirname5, join as join3, relative as
14141
14141
  // apps/body/dist/beeline-skill.js
14142
14142
  import { readFileSync as readFileSync3 } from "node:fs";
14143
14143
  import { resolve as resolve11 } from "node:path";
14144
+
14145
+ // packages/api-contract/dist/system-events.js
14146
+ var SERVER_EVENT_KINDS = [
14147
+ "joined",
14148
+ "schedule-ran",
14149
+ "corner-opened",
14150
+ "check-passed",
14151
+ "check-failed",
14152
+ "merged",
14153
+ "grant-decided"
14154
+ ];
14155
+ function isServerEventKind(value) {
14156
+ return SERVER_EVENT_KINDS.includes(value);
14157
+ }
14158
+ var RESUME_KINDS = ["grant-decided"];
14159
+ function isResumeKind(value) {
14160
+ return RESUME_KINDS.includes(value);
14161
+ }
14162
+
14163
+ // packages/api-contract/dist/agent-pairing-code.js
14164
+ var CURRENT_AGENT_PAIRING_CODE = /^[0-9A-F]{8}-[0-9A-F]{8}$/;
14165
+ var LEGACY_AGENT_PAIRING_CODE = /^BUZZ-(?:[0-9A-F]{8}-[0-9A-F]{8}|[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4})$/;
14166
+ function normalizeAgentPairingCode(value) {
14167
+ if (typeof value !== "string")
14168
+ return void 0;
14169
+ const normalized = value.trim().toUpperCase();
14170
+ return isAgentPairingCode(normalized) ? normalized : void 0;
14171
+ }
14172
+ function isAgentPairingCode(value) {
14173
+ return typeof value === "string" && (CURRENT_AGENT_PAIRING_CODE.test(value) || LEGACY_AGENT_PAIRING_CODE.test(value));
14174
+ }
14175
+
14176
+ // apps/body/dist/beeline-skill.js
14144
14177
  var USING_BEELINE_SKILL_NAME = "using-beeline";
14145
14178
  var BEELINE_ROOM_CAPABILITIES = [
14146
14179
  "The repository filesystem is read-only in this Room session.",
@@ -14151,7 +14184,7 @@ var BEELINE_ROOM_CAPABILITIES = [
14151
14184
  "Files and photos people share are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline); never fetch the reference URL.",
14152
14185
  "To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent attach_file with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote); it is attached to your reply. write_scratch_file produces the file, not a picture - turning it into a raster image needs a converter, which needs shell, which this Room does not have.",
14153
14186
  "To run something later or repeatedly, call beeline-agent create_schedule (interval in minutes or a 5-field cron, optional maxRuns); list_schedules / delete_schedule manage them.",
14154
- "To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (joined, schedule-ran, corner-opened, check-passed, check-failed, merged); each one then wakes you for a turn. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you.",
14187
+ `To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (${SERVER_EVENT_KINDS.join(", ")}); each one then wakes you for a turn. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. grant-decided carries the grant id and status and resumes the turn that asked for the grant.`,
14155
14188
  "To state something that happened so the Room and other agents can act on it, call beeline-agent emit_event with your own agent:<slug> kind, one sentence, and optionally the agent members to wake. Chains of events are bounded and a refused emit posts nothing.",
14156
14189
  "When repository work is needed, you MUST call beeline-agent open_corner with a name of at most three words - it titles the corner everywhere - and a complete objective of no more than 24 words. The host-governed call is the only way to start write work.",
14157
14190
  "When open_corner succeeds, the server posts the corner card: do not announce or restate the opening. End the turn with nothing more unless the person asked something else.",
@@ -15111,6 +15144,7 @@ async function resolveSharedSkillSources(operatorHome, names) {
15111
15144
  return resolveExplicitSkillSources(operatorHome, names);
15112
15145
  const seen = /* @__PURE__ */ new Set();
15113
15146
  const resolved = [];
15147
+ const skipped = [];
15114
15148
  for (const relativeRoot of OPERATOR_SKILL_SOURCE_DIRS) {
15115
15149
  const sourceRoot = resolve13(operatorHome, relativeRoot);
15116
15150
  const rootStats = await lstat(sourceRoot).catch(() => void 0);
@@ -15122,23 +15156,61 @@ async function resolveSharedSkillSources(operatorHome, names) {
15122
15156
  const candidate = resolve13(sourceRoot, entry);
15123
15157
  try {
15124
15158
  const candidateStats = await lstat(candidate);
15125
- if (!candidateStats.isDirectory() || candidateStats.isSymbolicLink())
15159
+ if (candidateStats.isSymbolicLink()) {
15160
+ const reason = await realpath(candidate).then(() => "symlinked directory", (error) => isMissingPathError(error) ? "dangling symlink" : "symlinked directory");
15161
+ skipped.push({
15162
+ path: candidate,
15163
+ reason
15164
+ });
15165
+ continue;
15166
+ }
15167
+ if (!candidateStats.isDirectory()) {
15168
+ skipped.push({ path: candidate, reason: "not an ordinary directory" });
15126
15169
  continue;
15170
+ }
15127
15171
  assertContained(sourceRoot, candidate);
15128
15172
  const skillMd = resolve13(candidate, "SKILL.md");
15129
- const skillStats = await lstat(skillMd);
15173
+ const skillStats = await lstat(skillMd).catch((error) => {
15174
+ if (isMissingPathError(error)) {
15175
+ skipped.push({ path: candidate, reason: "missing SKILL.md" });
15176
+ return void 0;
15177
+ }
15178
+ throw error;
15179
+ });
15180
+ if (!skillStats)
15181
+ continue;
15130
15182
  if (!skillStats.isFile() || skillStats.isSymbolicLink() || skillStats.nlink !== 1) {
15131
- throw new Error(`shared skill requires an ordinary SKILL.md: ${entry}`);
15183
+ skipped.push({ path: candidate, reason: "SKILL.md is not an ordinary file" });
15184
+ continue;
15132
15185
  }
15133
15186
  seen.add(entry);
15134
15187
  resolved.push({ name: entry, source: candidate });
15135
15188
  } catch (error) {
15189
+ if (isMissingPathError(error)) {
15190
+ skipped.push({ path: candidate, reason: "missing during discovery" });
15191
+ continue;
15192
+ }
15136
15193
  console.warn(`[body] skipping operator skill ${entry}:`, error);
15137
15194
  }
15138
15195
  }
15139
15196
  }
15197
+ logSkippedOperatorSkills(skipped);
15140
15198
  return resolved;
15141
15199
  }
15200
+ function isMissingPathError(error) {
15201
+ return typeof error === "object" && error !== null && error.code === "ENOENT";
15202
+ }
15203
+ function logSkippedOperatorSkills(skipped) {
15204
+ const totals = /* @__PURE__ */ new Map();
15205
+ for (const { path, reason } of skipped) {
15206
+ console.warn(`[body] skipping operator skill ${path}: ${reason}`);
15207
+ totals.set(reason, (totals.get(reason) ?? 0) + 1);
15208
+ }
15209
+ for (const [reason, count] of totals) {
15210
+ if (count > 1)
15211
+ console.warn(`[body] ${count} skill entries skipped: ${reason}`);
15212
+ }
15213
+ }
15142
15214
  async function resolveExplicitSkillSources(operatorHome, names) {
15143
15215
  const unique = [...new Set(names)];
15144
15216
  for (const name of unique) {
@@ -17411,24 +17483,6 @@ import { join as join6 } from "node:path";
17411
17483
  var SCHEDULE_SCHEDULER_NAME = "Beeline Scheduler";
17412
17484
  var SCHEDULE_RAN_VERB = "ran a schedule for";
17413
17485
 
17414
- // packages/api-contract/dist/system-events.js
17415
- var SERVER_EVENT_KINDS = [
17416
- "joined",
17417
- "schedule-ran",
17418
- "corner-opened",
17419
- "check-passed",
17420
- "check-failed",
17421
- "merged",
17422
- "grant-decided"
17423
- ];
17424
- function isServerEventKind(value) {
17425
- return SERVER_EVENT_KINDS.includes(value);
17426
- }
17427
- var RESUME_KINDS = ["grant-decided"];
17428
- function isResumeKind(value) {
17429
- return RESUME_KINDS.includes(value);
17430
- }
17431
-
17432
17486
  // apps/body/dist/monolith-room-turn.js
17433
17487
  function isRoomMcpPermissionRequest(request, mountedServers = ROOM_MOUNTED_MCP_SERVERS) {
17434
17488
  if (isSquireMcpPermissionRequest(request))
@@ -17501,6 +17555,15 @@ function agentReplyMentionIds(text2, roster, authorId) {
17501
17555
  aliases.set(key, entry);
17502
17556
  }
17503
17557
  }
17558
+ for (const member of roster.members) {
17559
+ if (member.identityId === authorId || !member.handle)
17560
+ continue;
17561
+ const handle = member.handle.trim().replace(/^@/, "").toLocaleLowerCase();
17562
+ const canonical = aliases.get(handle);
17563
+ const legacy = `a_${handle}`;
17564
+ if (canonical && !aliases.has(legacy))
17565
+ aliases.set(legacy, { ...canonical, display: legacy });
17566
+ }
17504
17567
  const mentioned = [];
17505
17568
  for (const { display, ids } of [...aliases.values()].sort((left, right) => right.display.length - left.display.length)) {
17506
17569
  if (ids.size !== 1)
@@ -19502,19 +19565,6 @@ import { chmod as chmod5, mkdir as mkdir14, readFile as readFile10, unlink as un
19502
19565
  import { dirname as dirname10, resolve as resolve22 } from "node:path";
19503
19566
  import { stdin as stdin3, stdout as stdout4 } from "node:process";
19504
19567
 
19505
- // packages/api-contract/dist/agent-pairing-code.js
19506
- var CURRENT_AGENT_PAIRING_CODE = /^[0-9A-F]{8}-[0-9A-F]{8}$/;
19507
- var LEGACY_AGENT_PAIRING_CODE = /^BUZZ-(?:[0-9A-F]{8}-[0-9A-F]{8}|[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4})$/;
19508
- function normalizeAgentPairingCode(value) {
19509
- if (typeof value !== "string")
19510
- return void 0;
19511
- const normalized = value.trim().toUpperCase();
19512
- return isAgentPairingCode(normalized) ? normalized : void 0;
19513
- }
19514
- function isAgentPairingCode(value) {
19515
- return typeof value === "string" && (CURRENT_AGENT_PAIRING_CODE.test(value) || LEGACY_AGENT_PAIRING_CODE.test(value));
19516
- }
19517
-
19518
19568
  // apps/body/dist/clack-support.js
19519
19569
  import { stdout as stdout2 } from "node:process";
19520
19570
  var FALLBACK_TERMINAL_COLUMNS = 80;
@@ -20820,39 +20870,59 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
20820
20870
  `, { mode: 384 });
20821
20871
  return true;
20822
20872
  }
20873
+ function siblingRevertNote(attempt, loadedRelease) {
20874
+ if (!loadedRelease || attempt?.releaseId !== loadedRelease || attempt.status !== "reverted") {
20875
+ return void 0;
20876
+ }
20877
+ return `shared update attempt reverted by sibling ${attempt.revertedBy ?? "unknown"}: ${attempt.failure ?? "no failure recorded"}`;
20878
+ }
20879
+ function describeOwnProof(proof) {
20880
+ return proof.modelAnswer === "unavailable" ? `this daemon's own probe reached the model boundary without an answer (${proof.modelAnswerReason ?? "no reason recorded"})` : "this daemon's own probe passed";
20881
+ }
20882
+ function attemptFailureText(error) {
20883
+ const message = error instanceof Error ? error.message : String(error);
20884
+ return message.replace(/\s+/g, " ").trim().slice(0, 300);
20885
+ }
20823
20886
  async function gateManagedSuccessor(input) {
20824
20887
  try {
20825
20888
  const attempt = await readUpdateAttempt(input.layout);
20826
20889
  const desiredRelease = attempt?.status === "pending" ? attempt.releaseId : void 0;
20827
20890
  if (!desiredRelease || !input.loadedRelease || desiredRelease !== input.loadedRelease) {
20828
- throw new Error(`successor loaded release ${input.loadedRelease ?? "unknown"}, not the pending desired release`);
20891
+ const reverted = siblingRevertNote(attempt, input.loadedRelease);
20892
+ throw new Error(reverted ? `successor release ${input.loadedRelease} was reverted before this daemon probed it: ${reverted}` : `successor loaded release ${input.loadedRelease ?? "unknown"}, not the pending desired release`);
20829
20893
  }
20830
20894
  const proof = await input.probe();
20831
20895
  if (!await proveLoadedReleaseReady(input.layout, input.runtimeDir, input.loadedRelease, {
20832
20896
  probeId: input.probeId,
20833
20897
  functionalProof: proof
20834
20898
  })) {
20835
- throw new Error(`successor release ${input.loadedRelease} did not produce functional proof`);
20899
+ const reverted = siblingRevertNote(await readUpdateAttempt(input.layout), input.loadedRelease);
20900
+ throw new Error(reverted ? `successor release ${input.loadedRelease} lost its attempt while this daemon proved it: ${describeOwnProof(proof)}; ${reverted}` : `successor release ${input.loadedRelease} did not produce functional proof`);
20836
20901
  }
20837
20902
  return { kind: "passed", proof };
20838
20903
  } catch (error) {
20839
20904
  return {
20840
20905
  kind: "failed",
20841
20906
  error,
20842
- rolledBack: await rollbackFailedSuccessor(input.layout, input.runtimeDir)
20907
+ rolledBack: await rollbackFailedSuccessor(input.layout, input.runtimeDir, {
20908
+ probeId: input.probeId,
20909
+ failure: attemptFailureText(error)
20910
+ })
20843
20911
  };
20844
20912
  }
20845
20913
  }
20846
- async function rollbackFailedSuccessor(layout, runtimeDir) {
20914
+ async function rollbackFailedSuccessor(layout, runtimeDir, cause = {}) {
20847
20915
  return withInstallLock(layout, async () => {
20848
20916
  const attempt = await readUpdateAttempt(layout);
20849
20917
  if (!attempt || attempt.status !== "pending")
20850
20918
  return false;
20919
+ const attribution = cause.probeId ? { revertedBy: cause.probeId } : {};
20851
20920
  if (!attempt.previousReleaseId) {
20852
20921
  await replaceUpdateAttempt(layout, {
20853
20922
  ...attempt,
20854
20923
  status: "reverted",
20855
- failure: "functional served-turn proof failed, but no previous release exists"
20924
+ failure: cause.failure ?? "functional served-turn proof failed, but no previous release exists",
20925
+ ...attribution
20856
20926
  });
20857
20927
  return false;
20858
20928
  }
@@ -20860,7 +20930,8 @@ async function rollbackFailedSuccessor(layout, runtimeDir) {
20860
20930
  await replaceUpdateAttempt(layout, {
20861
20931
  ...attempt,
20862
20932
  status: "reverted",
20863
- failure: "functional served-turn proof failed before confirmation"
20933
+ failure: cause.failure ?? "functional served-turn proof failed before confirmation",
20934
+ ...attribution
20864
20935
  });
20865
20936
  if (runtimeDir)
20866
20937
  await queueUpdateRollbackAlert(runtimeDir, attempt.releaseId);
@@ -21067,6 +21138,28 @@ import { resolve as resolve26 } from "node:path";
21067
21138
  var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
21068
21139
  var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
21069
21140
  var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
21141
+ var ACP_ERROR_CODE = /\bACP error (-\d+):/;
21142
+ var ACP_PROMPT_INACTIVITY = /\bACP session\/prompt timed out after \d+ms of inactivity\b/;
21143
+ function isServerInternalCode(code) {
21144
+ return code === -32603 || code <= -32e3 && code >= -32099;
21145
+ }
21146
+ function classifyAcpTurnFailure(error) {
21147
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : void 0;
21148
+ if (!message)
21149
+ return void 0;
21150
+ if (ACP_PROMPT_INACTIVITY.test(message))
21151
+ return { kind: "prompt-inactivity" };
21152
+ const code = Number(ACP_ERROR_CODE.exec(message)?.[1]);
21153
+ if (Number.isFinite(code) && isServerInternalCode(code))
21154
+ return { kind: "server-internal", code };
21155
+ return void 0;
21156
+ }
21157
+ function sameAcpTurnFailure(one, other) {
21158
+ if (one.kind === "server-internal") {
21159
+ return other.kind === "server-internal" && one.code === other.code;
21160
+ }
21161
+ return other.kind === one.kind;
21162
+ }
21070
21163
  var UpdateFunctionalProbeError = class extends Error {
21071
21164
  reason;
21072
21165
  code = "BEELINE_UPDATE_FUNCTIONAL_PROBE_FAILED";
@@ -21138,6 +21231,7 @@ async function runUpdateFunctionalProbe(input) {
21138
21231
  args: agentArgsWithModelSelection(selectedAgent, input.config.modelSelection)
21139
21232
  };
21140
21233
  let modelAnswer = {};
21234
+ let turnCompleted = true;
21141
21235
  if (input.config.bwrapPath) {
21142
21236
  const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
21143
21237
  const operatorHome = input.config.operatorHome ?? homedir10();
@@ -21199,7 +21293,11 @@ async function runUpdateFunctionalProbe(input) {
21199
21293
  ...refusal ? { providerRefusal: refusal } : {}
21200
21294
  });
21201
21295
  }
21202
- const current = await input.compareWithCurrentRelease(refusal);
21296
+ const current = await input.compareWithCurrentRelease({
21297
+ kind: "provider-refusal",
21298
+ reason: refusal.reason,
21299
+ refusal
21300
+ });
21203
21301
  if (current.kind !== "refused" || current.status !== refusal.status) {
21204
21302
  throw new UpdateFunctionalProbeError("turn-failed", `${detail}; ${describeCurrentReleaseOutcome(current)}`, { providerRefusal: refusal });
21205
21303
  }
@@ -21214,7 +21312,26 @@ async function runUpdateFunctionalProbe(input) {
21214
21312
  } catch (error) {
21215
21313
  if (error instanceof UpdateFunctionalProbeError)
21216
21314
  throw error;
21217
- throw new UpdateFunctionalProbeError("turn-failed", error instanceof Error ? error.message : String(error), { cause: error });
21315
+ const detail = error instanceof Error ? error.message : String(error);
21316
+ const failure = classifyAcpTurnFailure(error);
21317
+ if (!failure || !input.compareWithCurrentRelease) {
21318
+ throw new UpdateFunctionalProbeError("turn-failed", detail, { cause: error });
21319
+ }
21320
+ const current = await input.compareWithCurrentRelease({
21321
+ kind: "acp-turn-failure",
21322
+ reason: detail,
21323
+ failure
21324
+ });
21325
+ const currentFailure = current.kind === "unavailable" ? classifyAcpTurnFailure(current.reason) : void 0;
21326
+ if (!currentFailure || !sameAcpTurnFailure(failure, currentFailure)) {
21327
+ throw new UpdateFunctionalProbeError("turn-failed", `${detail}; ${describeCurrentReleaseOutcome(current)}`, { cause: error });
21328
+ }
21329
+ console.warn(`[body] update probe: the probe turn failed the same way on this release and the current release (${detail}); that failure is not this bundle's doing, so the probe passes as inconclusive`);
21330
+ turnCompleted = false;
21331
+ modelAnswer = {
21332
+ modelAnswer: "unavailable",
21333
+ modelAnswerReason: `${detail} (the current release fails the same way)`
21334
+ };
21218
21335
  }
21219
21336
  } catch (error) {
21220
21337
  if (error instanceof UpdateFunctionalProbeError)
@@ -21225,7 +21342,7 @@ async function runUpdateFunctionalProbe(input) {
21225
21342
  harness,
21226
21343
  sandboxed: Boolean(input.config.bwrapPath),
21227
21344
  sessionStarted: true,
21228
- turnCompleted: true,
21345
+ turnCompleted,
21229
21346
  nativeTools: [],
21230
21347
  ...modelAnswer
21231
21348
  };
@@ -21239,7 +21356,7 @@ async function runUpdateFunctionalProbe(input) {
21239
21356
  import { spawn as spawn7 } from "node:child_process";
21240
21357
  import { dirname as dirname14, join as join8 } from "node:path";
21241
21358
  init_self_update();
21242
- var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 8e4;
21359
+ var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 12e4;
21243
21360
  var UPDATE_PROBE_COMMAND = "update-probe";
21244
21361
  function parseReport(line) {
21245
21362
  let parsed;
@@ -21567,8 +21684,8 @@ async function runStoredDaemon(pathOrPointer) {
21567
21684
  releaseId: loadedRelease ?? "unknown",
21568
21685
  sandboxRequired: runtime.sandbox !== "off",
21569
21686
  ...currentReleaseId ? {
21570
- compareWithCurrentRelease: async (refusal) => {
21571
- console.warn(`[thin-core] successor probe refused by the provider (${refusal.reason}); probing the current release ${currentReleaseId} for the same refusal`);
21687
+ compareWithCurrentRelease: async (appeal) => {
21688
+ console.warn(`[thin-core] successor probe got no answer from the provider (${appeal.reason}); probing the current release ${currentReleaseId} for the same outcome`);
21572
21689
  await extendSystemdStartTimeout(CURRENT_RELEASE_PROBE_TIMEOUT_MS + 15e3);
21573
21690
  return probeReleaseInSubprocess({
21574
21691
  layout,
@@ -21614,7 +21731,12 @@ async function runStoredDaemon(pathOrPointer) {
21614
21731
  console.log(`[beeline] agent ${runtime.agent.publicKey} removed; runtime archived at ${archivedRuntime}`);
21615
21732
  }
21616
21733
  } catch (error) {
21617
- const rolledBack = successorRolledBack || layout && pendingSuccessor && !ready && await rollbackFailedSuccessor(layout, runtimeDir);
21734
+ const rolledBack = successorRolledBack || layout && pendingSuccessor && !ready && // Named, so a sibling daemon's journal can say whose failure reverted
21735
+ // the attempt it shares with this one.
21736
+ await rollbackFailedSuccessor(layout, runtimeDir, {
21737
+ probeId: runtime.agent.publicKey,
21738
+ failure: attemptFailureText(error)
21739
+ });
21618
21740
  if (rolledBack) {
21619
21741
  console.error("[thin-core] successor failed before READY; previous release restored once");
21620
21742
  const alertRoom = runtime.rooms[0]?.channelId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usebeeline",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "description": "Connect an AI coding agent to Beeline with one command.",
5
5
  "homepage": "https://usebeeline.app",
6
6
  "bugs": {