usebeeline 0.0.99 → 0.0.104

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 +218 -83
  2. package/package.json +1 -1
@@ -4389,44 +4389,44 @@ async function readUpdateAttempt(layout) {
4389
4389
  return void 0;
4390
4390
  }
4391
4391
  }
4392
- async function writeUpdateAttempt(layout, record2) {
4392
+ async function writeUpdateAttempt(layout, record3) {
4393
4393
  await mkdir15(join9(layout.releasesRoot, ".state"), { recursive: true });
4394
4394
  const path = updateAttemptPath(layout);
4395
4395
  const staged = `${path}.${process.pid}.tmp`;
4396
- await writeFile10(staged, `${JSON.stringify(record2, null, 2)}
4396
+ await writeFile10(staged, `${JSON.stringify(record3, null, 2)}
4397
4397
  `, { mode: 384 });
4398
4398
  await rename4(staged, path);
4399
4399
  }
4400
- async function replaceUpdateAttempt(layout, record2) {
4401
- await writeUpdateAttempt(layout, record2);
4400
+ async function replaceUpdateAttempt(layout, record3) {
4401
+ await writeUpdateAttempt(layout, record3);
4402
4402
  }
4403
- async function writeUpdateAttemptFixture(layout, record2) {
4403
+ async function writeUpdateAttemptFixture(layout, record3) {
4404
4404
  await writeUpdateAttempt(layout, {
4405
- ...record2,
4406
- version: record2.version ?? 1,
4407
- confirmBy: record2.confirmBy ?? record2.appliedAt + DEFAULT_UPDATE_CONFIRM_WINDOW_MS,
4408
- status: record2.status ?? "pending"
4405
+ ...record3,
4406
+ version: record3.version ?? 1,
4407
+ confirmBy: record3.confirmBy ?? record3.appliedAt + DEFAULT_UPDATE_CONFIRM_WINDOW_MS,
4408
+ status: record3.status ?? "pending"
4409
4409
  });
4410
4410
  }
4411
4411
  async function settleUpdateAttemptOnStart(layout, opts = {}) {
4412
- const record2 = await readUpdateAttempt(layout);
4413
- if (!record2 || record2.status !== "pending")
4412
+ const record3 = await readUpdateAttempt(layout);
4413
+ if (!record3 || record3.status !== "pending")
4414
4414
  return { kind: "none" };
4415
4415
  const now2 = opts.now ?? Date.now;
4416
- const confirmAt = opts.confirmWindowMs ? record2.appliedAt + opts.confirmWindowMs : record2.confirmBy;
4416
+ const confirmAt = opts.confirmWindowMs ? record3.appliedAt + opts.confirmWindowMs : record3.confirmBy;
4417
4417
  if (now2() <= confirmAt) {
4418
- return { kind: "pending", confirmAt, record: record2 };
4418
+ return { kind: "pending", confirmAt, record: record3 };
4419
4419
  }
4420
- if (record2.previousReleaseId) {
4421
- await rollbackToPreviousRelease(layout, record2.previousReleaseId);
4420
+ if (record3.previousReleaseId) {
4421
+ await rollbackToPreviousRelease(layout, record3.previousReleaseId);
4422
4422
  }
4423
4423
  await writeUpdateAttempt(layout, {
4424
- ...record2,
4424
+ ...record3,
4425
4425
  confirmBy: confirmAt,
4426
4426
  status: "reverted",
4427
4427
  failure: "update did not serve a verified turn before its confirmation deadline"
4428
4428
  });
4429
- return { kind: "rolled-back", record: record2 };
4429
+ return { kind: "rolled-back", record: record3 };
4430
4430
  }
4431
4431
  function describeIdentity(identity) {
4432
4432
  if (!identity)
@@ -6864,13 +6864,13 @@ function parseAvailableCommands(value) {
6864
6864
  for (const entry of value) {
6865
6865
  if (!entry || typeof entry !== "object")
6866
6866
  continue;
6867
- const record2 = entry;
6868
- const rawName = typeof record2.name === "string" ? record2.name.trim().replace(/^\/+/, "") : "";
6867
+ const record3 = entry;
6868
+ const rawName = typeof record3.name === "string" ? record3.name.trim().replace(/^\/+/, "") : "";
6869
6869
  if (!rawName || rawName.length > 80 || seen.has(rawName))
6870
6870
  continue;
6871
6871
  seen.add(rawName);
6872
- const description = typeof record2.description === "string" && record2.description.trim() ? record2.description.trim().slice(0, 300) : void 0;
6873
- const input = record2.input;
6872
+ const description = typeof record3.description === "string" && record3.description.trim() ? record3.description.trim().slice(0, 300) : void 0;
6873
+ const input = record3.input;
6874
6874
  const hintRaw = input && typeof input.hint === "string" ? input.hint : void 0;
6875
6875
  const inputHint = hintRaw && hintRaw.trim() ? hintRaw.trim().slice(0, 120) : void 0;
6876
6876
  result.push({
@@ -6901,7 +6901,8 @@ function isPiAcpHarness(agentLabel) {
6901
6901
  function withoutOneTrailingLineEnding(text2) {
6902
6902
  if (/\r?\n\r?\n$/.test(text2))
6903
6903
  return text2;
6904
- return text2.replace(/\r?\n$/, "");
6904
+ const stripped = text2.replace(/\r?\n$/, "");
6905
+ return stripped || text2;
6905
6906
  }
6906
6907
  function normalizeStreamDelta(text2, agentLabel) {
6907
6908
  return agentLabel && PI_ACP_HARNESS.test(agentLabel) ? withoutOneTrailingLineEnding(text2) : text2;
@@ -7727,12 +7728,12 @@ function parseAdvertisedConfigOptions(raw, preferredModelId, includeLaunchEffort
7727
7728
  for (const entry of Array.isArray(configOptions) ? configOptions : []) {
7728
7729
  if (!entry || typeof entry !== "object")
7729
7730
  continue;
7730
- const record2 = entry;
7731
- const id = record2.id;
7732
- const category = record2.category;
7731
+ const record3 = entry;
7732
+ const id = record3.id;
7733
+ const category = record3.category;
7733
7734
  if (typeof id !== "string" || typeof category !== "string")
7734
7735
  continue;
7735
- const rawChoices = Array.isArray(record2.options) ? record2.options : [];
7736
+ const rawChoices = Array.isArray(record3.options) ? record3.options : [];
7736
7737
  const choices = [];
7737
7738
  for (const rawChoice of rawChoices) {
7738
7739
  if (!rawChoice || typeof rawChoice !== "object")
@@ -7749,7 +7750,7 @@ function parseAdvertisedConfigOptions(raw, preferredModelId, includeLaunchEffort
7749
7750
  result.push({
7750
7751
  id,
7751
7752
  category,
7752
- ...typeof record2.currentValue === "string" ? { currentValue: record2.currentValue } : {},
7753
+ ...typeof record3.currentValue === "string" ? { currentValue: record3.currentValue } : {},
7753
7754
  options: category === "model" ? sortModelChoicesNewestFirst(choices) : choices
7754
7755
  });
7755
7756
  }
@@ -7857,8 +7858,8 @@ function credentialedPiCustomProviders(env) {
7857
7858
  };
7858
7859
  if (Array.isArray(providers)) {
7859
7860
  for (const provider of providers) {
7860
- const record2 = provider;
7861
- remember(record2?.name ?? record2?.id, record2);
7861
+ const record3 = provider;
7862
+ remember(record3?.name ?? record3?.id, record3);
7862
7863
  }
7863
7864
  } else if (providers && typeof providers === "object") {
7864
7865
  for (const [name, config] of Object.entries(providers)) {
@@ -8183,6 +8184,9 @@ function isAgentCommand(value) {
8183
8184
  return Boolean(source && typeof source.id === "string" && typeof source.authorId === "string" && typeof source.body === "string" && typeof source.createdAt === "number" && Array.isArray(source.attachments));
8184
8185
  }
8185
8186
 
8187
+ // packages/api-contract/dist/artifacts.js
8188
+ var ARTIFACT_MAXIMUM_BYTES = 2 * 1024 * 1024;
8189
+
8186
8190
  // packages/api-contract/dist/system-events.js
8187
8191
  var SERVER_EVENT_KINDS = [
8188
8192
  "joined",
@@ -18047,6 +18051,106 @@ var GrantRunnerServer = class {
18047
18051
  }
18048
18052
  };
18049
18053
 
18054
+ // apps/body/dist/connector-runner.js
18055
+ function toolName(call) {
18056
+ const raw = `${call.title ?? ""} ${call.kind ?? ""}`;
18057
+ const match = raw.match(/(?:^|__|\b)(use_credential|fetch_credential|grant_app_access|revoke_app_access)\b/);
18058
+ return match?.[1] ?? "";
18059
+ }
18060
+ var SQUIRE_EVENT_CLASS = {
18061
+ fetch_credential: "approval",
18062
+ // the credential was read in clear
18063
+ grant_app_access: "approval",
18064
+ // a host was added to a key
18065
+ revoke_app_access: "vault-change"
18066
+ // access removed on the owner's behalf
18067
+ };
18068
+ function record(call) {
18069
+ return call && typeof call === "object" ? call : {};
18070
+ }
18071
+ function serializedSize(value) {
18072
+ if (value == null)
18073
+ return 0;
18074
+ if (typeof value === "string")
18075
+ return value.length;
18076
+ try {
18077
+ return JSON.stringify(value).length;
18078
+ } catch {
18079
+ return 0;
18080
+ }
18081
+ }
18082
+ function squireUsageFromToolCall(call) {
18083
+ const tool = toolName(call);
18084
+ if (!tool)
18085
+ return void 0;
18086
+ const args = record(call.rawInput);
18087
+ const result = record(call.content ?? call.rawOutput);
18088
+ const resultPayload = record(result.content ?? result);
18089
+ const url = typeof args.url === "string" ? args.url : "";
18090
+ const method = typeof args.method === "string" ? args.method.toUpperCase() : tool;
18091
+ const operation = url ? `${method} ${url}` : `${method} ${String(args.reference ?? "")}`.trim();
18092
+ const statusCode = typeof resultPayload.status === "number" ? resultPayload.status : /failed|error|denied/i.test(call.status ?? "") ? 0 : 200;
18093
+ return {
18094
+ ref: String(args.reference ?? args.service ?? ""),
18095
+ service: typeof args.service === "string" ? args.service : null,
18096
+ operation: operation.slice(0, 300),
18097
+ statusCode,
18098
+ bytes: serializedSize(resultPayload.body ?? resultPayload),
18099
+ ...typeof args.grant_id === "string" ? { grantId: args.grant_id } : {},
18100
+ ...SQUIRE_EVENT_CLASS[tool] ? { eventClass: SQUIRE_EVENT_CLASS[tool] } : {}
18101
+ };
18102
+ }
18103
+ var ConnectorUsageRecorder = class {
18104
+ batches = /* @__PURE__ */ new Map();
18105
+ turns = /* @__PURE__ */ new Map();
18106
+ record(turn, usage2) {
18107
+ if (!usage2.ref)
18108
+ return;
18109
+ const existing = this.batches.get(turn.requestId) ?? [];
18110
+ existing.push(usage2);
18111
+ this.batches.set(turn.requestId, existing);
18112
+ if (!this.turns.has(turn.requestId))
18113
+ this.turns.set(turn.requestId, turn);
18114
+ }
18115
+ /** Everything recorded for this turn so far; used by tests and diagnostics. */
18116
+ pending(requestId) {
18117
+ return this.batches.get(requestId) ?? [];
18118
+ }
18119
+ /**
18120
+ * Publish the turn's whole batch as ONE operation call and clear it.
18121
+ * Returns the published record count (0 when nothing was recorded or the
18122
+ * publish failed — a failed usage report never fails the agent turn).
18123
+ */
18124
+ async flush(api, requestId) {
18125
+ const usage2 = this.batches.get(requestId);
18126
+ const turn = this.turns.get(requestId);
18127
+ if (!usage2?.length || !turn)
18128
+ return 0;
18129
+ this.batches.delete(requestId);
18130
+ this.turns.delete(requestId);
18131
+ try {
18132
+ await api.execute("postConnectionUsage", {
18133
+ requestId: turn.requestId,
18134
+ agentId: turn.agentId,
18135
+ ...turn.roomId ? { roomId: turn.roomId } : {},
18136
+ ...turn.cornerId ? { cornerId: turn.cornerId } : {},
18137
+ usage: usage2
18138
+ });
18139
+ return usage2.length;
18140
+ } catch (error) {
18141
+ console.error("[connector] connection usage report failed:", error);
18142
+ return 0;
18143
+ }
18144
+ }
18145
+ };
18146
+ function captureConnectionUsage(recorder, turn, calls) {
18147
+ for (const call of calls) {
18148
+ const usage2 = squireUsageFromToolCall(call);
18149
+ if (usage2)
18150
+ recorder.record(turn, usage2);
18151
+ }
18152
+ }
18153
+
18050
18154
  // apps/body/dist/server-command-intake.js
18051
18155
  import { randomUUID as randomUUID2 } from "node:crypto";
18052
18156
  import { mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
@@ -18312,6 +18416,20 @@ description: How to answer inside a Beeline Room.
18312
18416
  # Using Beeline
18313
18417
 
18314
18418
  You are answering inside a Room whose filesystem is read-only. ${BEELINE_ROOM_CAPABILITIES}
18419
+
18420
+ ## Tools and the Workbench
18421
+
18422
+ A **tool** is something you can use once a human pairs it; a **key** is the credential that tool holds for that human. You spend a key through the mounted connector and never see the credential itself.
18423
+
18424
+ This build knows one tool: Trusty Squire - vaulted credentials and a browser that signs in for you. Wallet and Tailscale are named in the Workbench but not yet available.
18425
+
18426
+ You NEVER pair a tool and never ask for a raw credential in chat. When a task needs one you do not have, say so plainly, name the tool that would solve it, and tell the person exactly where to go: Settings \u2192 Workbench \u2192 Tools.
18427
+
18428
+ Keys are sovereign: they belong to the human who provisioned them. You cannot use another member's key and must not ask a member to share one.
18429
+
18430
+ ## Showing a mock
18431
+
18432
+ When a design decision needs eyes, show it instead of describing it. Build ONE self-contained HTML page and post it with beeline-agent post_artifact (mime "text/html", pass the document as html). Everything is inline: a single <style> element for all CSS and data: URLs for any image - no script, no external dependencies, no network references. The validator refuses every <script>, <link>, <iframe>, <object>, <embed>, <form>, inline event handler, and http(s) URL, so a page that reaches for the network never posts. Use the Obsidian Refined tokens: grayscale surfaces, one brass accent #d7af5f, 3px radii, IBM Plex Sans/Mono type. Lay the user stories out as frames - one bordered, labelled block per story, so each story can be judged on its own. After posting, ask for feedback here in the corner: the artifact is the thing people react to, not your prose.
18315
18433
  `;
18316
18434
  }
18317
18435
  function beelineReviewSkillMarkdown(releaseId) {
@@ -18437,8 +18555,8 @@ function openRouterModelId(model, env = {}) {
18437
18555
  }
18438
18556
  function parseOpenRouterEndpoints(payload) {
18439
18557
  const data = payload?.data;
18440
- const record2 = data && typeof data === "object" ? data : {};
18441
- const raw = Array.isArray(record2.endpoints) ? record2.endpoints : [];
18558
+ const record3 = data && typeof data === "object" ? data : {};
18559
+ const raw = Array.isArray(record3.endpoints) ? record3.endpoints : [];
18442
18560
  const endpoints = [];
18443
18561
  for (const entry of raw) {
18444
18562
  if (!entry || typeof entry !== "object")
@@ -18453,11 +18571,11 @@ function parseOpenRouterEndpoints(payload) {
18453
18571
  const supported = Array.isArray(endpoint2.supported_parameters) ? endpoint2.supported_parameters : [];
18454
18572
  endpoints.push({ provider, uptime, contextLength, tools: supported.includes("tools") });
18455
18573
  }
18456
- const architecture = record2.architecture && typeof record2.architecture === "object" ? record2.architecture : {};
18574
+ const architecture = record3.architecture && typeof record3.architecture === "object" ? record3.architecture : {};
18457
18575
  const modalities = Array.isArray(architecture.input_modalities) ? architecture.input_modalities.filter((value) => typeof value === "string" && value.length > 0) : void 0;
18458
18576
  return {
18459
18577
  endpoints,
18460
- contextLength: typeof record2.context_length === "number" ? record2.context_length : advertisedContextLength(endpoints),
18578
+ contextLength: typeof record3.context_length === "number" ? record3.context_length : advertisedContextLength(endpoints),
18461
18579
  inputModalities: modalities?.length ? modalities : void 0
18462
18580
  };
18463
18581
  }
@@ -19904,8 +20022,8 @@ function toolCallText(content) {
19904
20022
  }
19905
20023
  if (!value || typeof value !== "object")
19906
20024
  return;
19907
- const record2 = value;
19908
- for (const [key, nested] of Object.entries(record2)) {
20025
+ const record3 = value;
20026
+ for (const [key, nested] of Object.entries(record3)) {
19909
20027
  if (["text", "content", "message", "error", "output"].includes(key.toLowerCase())) {
19910
20028
  walk(nested, depth + 1);
19911
20029
  }
@@ -20204,8 +20322,8 @@ function shellPayload(toolCall) {
20204
20322
  return true;
20205
20323
  if (!rawInput || typeof rawInput !== "object" || Array.isArray(rawInput))
20206
20324
  return false;
20207
- const record2 = rawInput;
20208
- return typeof record2.command === "string" || typeof record2.cmd === "string";
20325
+ const record3 = rawInput;
20326
+ return typeof record3.command === "string" || typeof record3.cmd === "string";
20209
20327
  }
20210
20328
  var ROOM_MOUNTED_MCP_SERVERS = [
20211
20329
  READ_ONLY_MCP_SERVER_NAME,
@@ -20217,10 +20335,10 @@ var TOOL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
20217
20335
  function permissionRecord(rawInput) {
20218
20336
  return rawInput && typeof rawInput === "object" && !Array.isArray(rawInput) ? rawInput : void 0;
20219
20337
  }
20220
- function dispatchedPayloadIsShell(record2) {
20221
- if (!record2)
20338
+ function dispatchedPayloadIsShell(record3) {
20339
+ if (!record3)
20222
20340
  return false;
20223
- return DISPATCHER_TOOL_INPUT_KEYS.some((key) => key in record2 && shellPayload({ rawInput: record2[key] }));
20341
+ return DISPATCHER_TOOL_INPUT_KEYS.some((key) => key in record3 && shellPayload({ rawInput: record3[key] }));
20224
20342
  }
20225
20343
  function toolIdentityCandidates(toolCall) {
20226
20344
  const candidates = [];
@@ -20229,9 +20347,9 @@ function toolIdentityCandidates(toolCall) {
20229
20347
  candidates.push(value.trim());
20230
20348
  };
20231
20349
  add(toolCall?.title);
20232
- const record2 = permissionRecord(toolCall?.rawInput);
20350
+ const record3 = permissionRecord(toolCall?.rawInput);
20233
20351
  for (const key of DISPATCHER_TOOL_NAME_KEYS)
20234
- add(record2?.[key]);
20352
+ add(record3?.[key]);
20235
20353
  return candidates;
20236
20354
  }
20237
20355
  function normalizedServerName(name) {
@@ -20241,8 +20359,8 @@ function resolveMountedMcpToolCall(request, mountedServers) {
20241
20359
  const toolCall = request.toolCall;
20242
20360
  if (shellPayload(toolCall))
20243
20361
  return void 0;
20244
- const record2 = permissionRecord(toolCall?.rawInput);
20245
- if (dispatchedPayloadIsShell(record2))
20362
+ const record3 = permissionRecord(toolCall?.rawInput);
20363
+ if (dispatchedPayloadIsShell(record3))
20246
20364
  return void 0;
20247
20365
  const candidates = toolIdentityCandidates(toolCall);
20248
20366
  for (const server of mountedServers) {
@@ -20519,25 +20637,25 @@ async function explainEmptyAgentTurn(input) {
20519
20637
  const streamFact = describeEmptyTurn(input.result, input.agentLabel);
20520
20638
  if (!isPiAcpHarness(input.agentLabel))
20521
20639
  return { reason: streamFact };
20522
- const record2 = await readPiTurnRecord({ agentEnv: input.agentEnv, sessionId: input.sessionId });
20523
- if (!record2)
20640
+ const record3 = await readPiTurnRecord({ agentEnv: input.agentEnv, sessionId: input.sessionId });
20641
+ if (!record3)
20524
20642
  return { reason: `pi left no readable turn record; ${streamFact}` };
20525
- switch (record2.kind) {
20643
+ switch (record3.kind) {
20526
20644
  case "error":
20527
- return { reason: record2.reason, record: record2 };
20645
+ return { reason: record3.reason, record: record3 };
20528
20646
  case "empty":
20529
20647
  return {
20530
- reason: `the model ended its turn with no text (stop reason ${record2.stopReason})`,
20531
- record: record2
20648
+ reason: `the model ended its turn with no text (stop reason ${record3.stopReason})`,
20649
+ record: record3
20532
20650
  };
20533
20651
  case "answer":
20534
20652
  return {
20535
- recoveredText: record2.text,
20653
+ recoveredText: record3.text,
20536
20654
  reason: "pi recorded the answer but the ACP stream delivered no text",
20537
- record: record2
20655
+ record: record3
20538
20656
  };
20539
20657
  case "missing":
20540
- return { reason: `pi recorded no assistant message for this turn; ${streamFact}`, record: record2 };
20658
+ return { reason: `pi recorded no assistant message for this turn; ${streamFact}`, record: record3 };
20541
20659
  }
20542
20660
  }
20543
20661
  function shouldRetryEmptyTurn(explanation) {
@@ -20564,10 +20682,10 @@ function turnFailureReasonWithProvider(reason, providers) {
20564
20682
  const served = describeTurnProviders(providers);
20565
20683
  return served ? `${reason} \xB7 ${served}` : reason;
20566
20684
  }
20567
- function isAccountOrProviderRefusal(record2) {
20568
- if (!record2 || record2.kind !== "error" || record2.status === void 0)
20685
+ function isAccountOrProviderRefusal(record3) {
20686
+ if (!record3 || record3.kind !== "error" || record3.status === void 0)
20569
20687
  return false;
20570
- return [401, 402, 403, 407, 408, 429].includes(record2.status) || record2.status >= 500;
20688
+ return [401, 402, 403, 407, 408, 429].includes(record3.status) || record3.status >= 500;
20571
20689
  }
20572
20690
 
20573
20691
  // apps/body/dist/response-directives.js
@@ -20824,11 +20942,11 @@ var TurnTrace = class {
20824
20942
  this.end(phase);
20825
20943
  this.promptSettled();
20826
20944
  }
20827
- const record2 = this.snapshot(outcome, reason);
20828
- await this.options.sink?.write(record2).catch((error) => {
20945
+ const record3 = this.snapshot(outcome, reason);
20946
+ await this.options.sink?.write(record3).catch((error) => {
20829
20947
  console.error("[thin-core] turn trace write failed:", error);
20830
20948
  });
20831
- return record2;
20949
+ return record3;
20832
20950
  }
20833
20951
  };
20834
20952
  function round(ms) {
@@ -20853,9 +20971,9 @@ function formatTurnAttempt(attempt) {
20853
20971
  parts.push(`${attempt.toolCalls} tool call${attempt.toolCalls === 1 ? "" : "s"}`);
20854
20972
  return parts.join(" \xB7 ");
20855
20973
  }
20856
- function formatTurnTraceLine(record2) {
20857
- const head = `${record2.surface} ${record2.roomId} turn ${record2.requestId} ${record2.outcome} ${formatDuration(record2.totalMs)}`;
20858
- return [head, ...record2.attempts.map((attempt) => formatTurnAttempt(attempt))].join("\n ");
20974
+ function formatTurnTraceLine(record3) {
20975
+ const head = `${record3.surface} ${record3.roomId} turn ${record3.requestId} ${record3.outcome} ${formatDuration(record3.totalMs)}`;
20976
+ return [head, ...record3.attempts.map((attempt) => formatTurnAttempt(attempt))].join("\n ");
20859
20977
  }
20860
20978
  function turnTraceDirectory(runtimeDir) {
20861
20979
  return resolve17(runtimeDir, "turn-traces");
@@ -20877,16 +20995,16 @@ var TurnTraceFile = class {
20877
20995
  path(now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))()) {
20878
20996
  return resolve17(this.directory, traceFileName(now2));
20879
20997
  }
20880
- write(record2) {
20998
+ write(record3) {
20881
20999
  this.tail = this.tail.catch(() => void 0).then(async () => {
20882
21000
  const now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))();
20883
21001
  const path = this.path(now2);
20884
21002
  await mkdir8(this.directory, { recursive: true, mode: 448 });
20885
- await appendFile(path, `${JSON.stringify(record2)}
21003
+ await appendFile(path, `${JSON.stringify(record3)}
20886
21004
  `, { mode: 384 });
20887
21005
  await this.prune(now2);
20888
21006
  const log2 = this.options.log ?? ((line) => console.log(line));
20889
- log2(`[thin-core] turn trace ${formatTurnTraceLine(record2)}${this.announced ? "" : `
21007
+ log2(`[thin-core] turn trace ${formatTurnTraceLine(record3)}${this.announced ? "" : `
20890
21008
  recorded in ${path}`}`);
20891
21009
  this.announced = true;
20892
21010
  });
@@ -22100,6 +22218,11 @@ function cornerReviewerInstruction(input) {
22100
22218
  const headSha = input.headSha ?? "<head sha>";
22101
22219
  return `Checks are green on PR #${number} at ${headSha}. Review it now with the beeline-review skill against that exact head. FAIL: reply \`@${author}\` with the confirmed findings to fix. PASS: call the approve_merge tool for ${headSha}, then reply \`@${author} approved ${headSha}, merge\`. Never merge yourself. Never say you are holding or waiting for checks.`;
22102
22220
  }
22221
+ function cornerSelfReviewerInstruction(input) {
22222
+ if (!input.reviewerHandle || !input.agentHandle || !input.openedByAgent || input.agentHandle.replace(/^@/, "") !== input.reviewerHandle.replace(/^@/, ""))
22223
+ return void 0;
22224
+ return "You are this Room's reviewer, so your own pull request needs no review: do not request one, do not tag any agent for review, and merge yourself with gh once checks pass and no hold exists.";
22225
+ }
22103
22226
  var CORNER_AUTHOR_CONTRACT = `The objective text is the user's ask. Keep it verbatim in your head and do not reinterpret it.
22104
22227
  Before any code, write its end-user story in one sentence: "a person who does X sees Y".
22105
22228
  If the objective reports a defect, reproduce it first at the layer where it lives: the command, request, or tap sequence, and what was observed.
@@ -22159,7 +22282,7 @@ function serialized(value) {
22159
22282
  return "";
22160
22283
  }
22161
22284
  }
22162
- function record(value) {
22285
+ function record2(value) {
22163
22286
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
22164
22287
  }
22165
22288
  function clampBytes(value, maxBytes) {
@@ -22199,7 +22322,7 @@ function filePaths(value, worktreePath) {
22199
22322
  candidate.forEach((entry) => visit(entry));
22200
22323
  return;
22201
22324
  }
22202
- const object = record(candidate);
22325
+ const object = record2(candidate);
22203
22326
  if (object)
22204
22327
  Object.entries(object).forEach(([entryKey, entry]) => visit(entry, entryKey));
22205
22328
  };
@@ -22207,9 +22330,9 @@ function filePaths(value, worktreePath) {
22207
22330
  return [...paths];
22208
22331
  }
22209
22332
  function resultStatus(call) {
22210
- const content = record(call.content) ?? (typeof call.content === "string" ? (() => {
22333
+ const content = record2(call.content) ?? (typeof call.content === "string" ? (() => {
22211
22334
  try {
22212
- return record(JSON.parse(call.content));
22335
+ return record2(JSON.parse(call.content));
22213
22336
  } catch {
22214
22337
  return void 0;
22215
22338
  }
@@ -22224,7 +22347,7 @@ function resultStatus(call) {
22224
22347
  return /(?:failed|error|denied)/i.test(call.status ?? "") ? "error" : "ok";
22225
22348
  }
22226
22349
  function toolArguments(call) {
22227
- const raw = record(call.rawInput);
22350
+ const raw = record2(call.rawInput);
22228
22351
  const command = typeof call.rawInput === "string" ? call.rawInput : typeof raw?.command === "string" ? raw.command : typeof raw?.cmd === "string" ? raw.cmd : void 0;
22229
22352
  if (command)
22230
22353
  return { command: clampBytes(redactToolDetail(command), TOOL_ARGUMENT_MAX_BYTES) };
@@ -22462,7 +22585,8 @@ var MonolithCornerTurnLoop = class {
22462
22585
  headSha: restore.lifecycle?.pr?.headSha
22463
22586
  });
22464
22587
  }
22465
- this.cornerTurnEndNudge = reviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle);
22588
+ const selfReviewerInstruction = cornerSelfReviewerInstruction(reviewerInput);
22589
+ this.cornerTurnEndNudge = reviewerInstruction ?? selfReviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle);
22466
22590
  await mkdir12(this.options.worktreePath, { recursive: true });
22467
22591
  const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
22468
22592
  const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
@@ -22612,10 +22736,10 @@ var MonolithCornerTurnLoop = class {
22612
22736
  ...reviewerInstruction ? [reviewerInstruction] : [
22613
22737
  configuration.reviewerHandle ? `Once the pull request exists, reply with its full URL and end the turn; do not tag the reviewer, check, or wait for CI.` : 'Once the pull request exists, reply only with its full URL and end the turn; do not check or wait for CI. On a later checks turn, call pr_checks_status. Merge only when checks="passed", held=false, and approvalPending=false; if checks="unknown", reply in this corner with the tool reason and stop instead of retrying. Only a later explicit human resume clears a hold.',
22614
22738
  CORNER_AUTHOR_CONTRACT,
22615
- cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle)
22739
+ selfReviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle)
22616
22740
  ],
22617
22741
  "Do not tag the user when a corner turn finishes: the server posts the merge summary card and its push already cover completion. Tag a human only mid-turn, and only when you need a decision or input.",
22618
- "Never restate server check or merge notes. On a checks turn, say nothing unless you merge or push a fix, then use one short line. Never merge while approvalPending is true. When approval is pending, wait for the reviewer to tag you. Never merge another pull request."
22742
+ "Never restate server check or merge notes. On a checks turn, say nothing unless you merge or push a fix, then use one short line. Never merge while approvalPending is true. When approval is pending, wait for the reviewer to tag you. Never merge another pull request. Never create a schedule to poll pr_checks_status or the merge gate: the green transition wakes the reviewer and the reviewer's approval tag wakes you, and tagging any agent other than the configured reviewer cannot clear the gate. If a schedule wakes you in this corner anyway, follow the same rule as a checks turn: say nothing unless you merge, push a fix, or report a genuinely new blocker."
22619
22743
  ] : [
22620
22744
  "This is a chat-only corner with no repository or GitHub workflow.",
22621
22745
  "Work in this corner's writable workspace. Use write_scratch_file or ordinary tools to create files, then attach_file to send them back to the corner.",
@@ -22781,7 +22905,7 @@ ${trigger}`,
22781
22905
  const pendingToolActivities = /* @__PURE__ */ new Map();
22782
22906
  let lastNarratedToolCall;
22783
22907
  let activityAttempt = 0;
22784
- const publishToolCalls = (calls, settledOnly) => {
22908
+ const publishToolCalls = (calls, settledOnly, exceptKey) => {
22785
22909
  calls.forEach((call, index) => {
22786
22910
  const key = `${activityAttempt}:${toolCallKey(call, index)}`;
22787
22911
  if (settledOnly && !observedToolCalls.has(key)) {
@@ -22791,7 +22915,7 @@ ${trigger}`,
22791
22915
  if (narration2)
22792
22916
  lastNarratedToolCall = key;
22793
22917
  }
22794
- if (settledOnly || publishedToolCalls.has(key) || !toolCallSettled(call))
22918
+ if (settledOnly || publishedToolCalls.has(key) || !toolCallSettled(call) || key === exceptKey)
22795
22919
  return;
22796
22920
  publishedToolCalls.add(key);
22797
22921
  const narration = pendingToolNarrations.get(key) ?? "";
@@ -22827,12 +22951,18 @@ ${trigger}`,
22827
22951
  });
22828
22952
  });
22829
22953
  };
22830
- const flushToolCalls = async (calls, finalReply) => {
22954
+ const flushToolCalls = async (calls, finalReply, final = false) => {
22831
22955
  await this.activityTail;
22832
22956
  if (lastNarratedToolCall && pendingToolNarrations.get(lastNarratedToolCall) === finalReply)
22833
22957
  pendingToolNarrations.delete(lastNarratedToolCall);
22834
22958
  publishToolCalls(calls, false);
22835
22959
  await this.activityTail;
22960
+ if (this.options.connectorUsage) {
22961
+ captureConnectionUsage(this.options.connectorUsage, { requestId, agentId: this.agent.publicKey, cornerId }, calls);
22962
+ if (final)
22963
+ await this.options.connectorUsage.flush(api, requestId);
22964
+ }
22965
+ await this.activityTail;
22836
22966
  publishToolCalls(calls, false);
22837
22967
  await this.activityTail;
22838
22968
  };
@@ -22864,6 +22994,7 @@ ${trigger}`,
22864
22994
  }, void 0, (calls) => {
22865
22995
  trace.toolCalls(calls);
22866
22996
  publishToolCalls(calls, true);
22997
+ publishToolCalls(calls, false, lastNarratedToolCall);
22867
22998
  });
22868
22999
  };
22869
23000
  let result = await runPrompt();
@@ -22901,7 +23032,7 @@ ${trigger}`,
22901
23032
  if (!reply && explained?.recoveredText)
22902
23033
  reply = durableReplyText(explained.recoveredText);
22903
23034
  reply = [replyBeforeNudge, reply].filter(Boolean).join("\n\n");
22904
- await flushToolCalls(result.toolCalls, reply);
23035
+ await flushToolCalls(result.toolCalls, reply, true);
22905
23036
  for (const call of result.toolCalls) {
22906
23037
  const failure = toolCallFailureLine(call);
22907
23038
  if (failure)
@@ -23632,6 +23763,8 @@ var RoomRuntimeCoordinator = class {
23632
23763
  /** One command-grant runner per daemon; Rooms and corners register their checkouts on it. */
23633
23764
  grantRunner;
23634
23765
  grantRunnerServer;
23766
+ /** Connection usage capture, batched per agent turn. */
23767
+ connectorUsage;
23635
23768
  constructor(runtime, configPath, baseConfig, options) {
23636
23769
  this.configPath = configPath;
23637
23770
  this.baseConfig = baseConfig;
@@ -23646,6 +23779,7 @@ var RoomRuntimeCoordinator = class {
23646
23779
  agentId: this.agent.publicKey
23647
23780
  });
23648
23781
  this.grantRunnerServer = new GrantRunnerServer(this.grantRunner);
23782
+ this.connectorUsage = new ConnectorUsageRecorder();
23649
23783
  this.watchdogStaleMs = options.watchdogStaleMs ?? DEFAULT_ROOM_WATCHDOG_STALE_MS;
23650
23784
  this.reconcileHeartbeatMs = options.reconcileHeartbeatMs ?? DEFAULT_RECONCILE_HEARTBEAT_MS;
23651
23785
  this.drainDeadlineMs = options.drainDeadlineMs ?? DEFAULT_DRAIN_DEADLINE_MS;
@@ -23962,6 +24096,7 @@ var RoomRuntimeCoordinator = class {
23962
24096
  cornerId: corner.cornerId,
23963
24097
  grantRunner: this.grantRunner,
23964
24098
  ...grantRunnerEndpoint ? { grantRunnerEndpoint } : {},
24099
+ connectorUsage: this.connectorUsage,
23965
24100
  parentRoomId: corner.parentRoomId,
23966
24101
  workspaceId: this.runtime.communityId,
23967
24102
  ...corner.openedBy ? { openedBy: corner.openedBy } : {},
@@ -25668,7 +25803,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
25668
25803
  libDir: resolve25(this.#layout.releasesRoot, this.#loadedRelease)
25669
25804
  }).catch(() => void 0) ?? {};
25670
25805
  const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
25671
- const record2 = {
25806
+ const record3 = {
25672
25807
  version: 1,
25673
25808
  from,
25674
25809
  to,
@@ -25679,7 +25814,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
25679
25814
  status: "pending",
25680
25815
  ...this.#requiredProbeIds.length > 0 ? { requiredProbeIds: this.#requiredProbeIds, confirmedProbeIds: [] } : {}
25681
25816
  };
25682
- await writeUpdateAttempt(this.#layout, record2);
25817
+ await writeUpdateAttempt(this.#layout, record3);
25683
25818
  } else if (this.#requiredProbeIds.length > 0) {
25684
25819
  const requiredProbeIds = [
25685
25820
  .../* @__PURE__ */ new Set([...attempt.requiredProbeIds ?? [], ...this.#requiredProbeIds])
@@ -26198,11 +26333,11 @@ async function readFailureRecord(runtimeDir) {
26198
26333
  return void 0;
26199
26334
  }
26200
26335
  }
26201
- async function writeFailureRecord(runtimeDir, record2) {
26336
+ async function writeFailureRecord(runtimeDir, record3) {
26202
26337
  const path = daemonFailurePath(runtimeDir);
26203
26338
  const staged = `${path}.${process.pid}.tmp`;
26204
26339
  await mkdir19(dirname15(path), { recursive: true, mode: 448 });
26205
- await writeFile14(staged, `${JSON.stringify(record2, null, 2)}
26340
+ await writeFile14(staged, `${JSON.stringify(record3, null, 2)}
26206
26341
  `, { mode: 384 });
26207
26342
  await rename6(staged, path);
26208
26343
  }
@@ -26210,13 +26345,13 @@ async function recordDaemonStartFailure(runtimeDir, error, now2 = Date.now()) {
26210
26345
  const prior = await readFailureRecord(runtimeDir);
26211
26346
  const failures = [...prior?.failures ?? [], now2].filter((failure) => now2 - failure <= DAEMON_FAILURE_WINDOW_MS);
26212
26347
  const distressed = failures.length >= DAEMON_FAILURE_LIMIT;
26213
- const record2 = {
26348
+ const record3 = {
26214
26349
  version: 1,
26215
26350
  failures,
26216
26351
  lastError: (error instanceof Error ? error.message : String(error)).slice(0, 1e3),
26217
26352
  ...distressed ? { distressedAt: now2 } : {}
26218
26353
  };
26219
- await writeFailureRecord(runtimeDir, record2);
26354
+ await writeFailureRecord(runtimeDir, record3);
26220
26355
  return { distressed, count: failures.length, path: daemonFailurePath(runtimeDir) };
26221
26356
  }
26222
26357
  async function clearDaemonStartFailures(runtimeDir) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usebeeline",
3
- "version": "0.0.99",
3
+ "version": "0.0.104",
4
4
  "description": "Connect an AI coding agent to Beeline with one command.",
5
5
  "homepage": "https://usebeeline.app",
6
6
  "bugs": {