@coreplane/switchboard 1.221.1 → 1.223.0

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/cli.js CHANGED
@@ -2858,8 +2858,24 @@ function validateConfig(cfg) {
2858
2858
  if (cfg.routing !== void 0) validateRouting(cfg.routing, cfg.providers);
2859
2859
  if (cfg.artifacts !== void 0) validateArtifacts(cfg.artifacts);
2860
2860
  if (cfg.harness !== void 0) validateHarness(cfg.harness);
2861
+ if (cfg.pi !== void 0) validatePi(cfg.pi);
2861
2862
  validateDashboardConfig(cfg.dashboard);
2862
2863
  }
2864
+ function validatePi(pi) {
2865
+ if (typeof pi !== "object" || pi === null || Array.isArray(pi)) throw new Error("config.yaml: pi must be a mapping");
2866
+ for (const key of unknownKeys2(pi, PI_KEYS)) throw new Error(`config.yaml: pi.${key} is not a known key`);
2867
+ const { compaction } = pi;
2868
+ if (compaction === void 0) return;
2869
+ if (typeof compaction !== "object" || compaction === null || Array.isArray(compaction))
2870
+ throw new Error("config.yaml: pi.compaction must be a mapping");
2871
+ for (const key of unknownKeys2(compaction, PI_COMPACTION_KEYS))
2872
+ throw new Error(`config.yaml: pi.compaction.${key} is not a known key`);
2873
+ for (const key of Object.keys(PI_COMPACTION_KEYS)) {
2874
+ const value = compaction[key];
2875
+ if (value !== void 0 && (typeof value !== "number" || !Number.isInteger(value) || value <= 0))
2876
+ throw new Error(`config.yaml: pi.compaction.${key} must be a positive integer (tokens)`);
2877
+ }
2878
+ }
2863
2879
  function validateHarness(harness) {
2864
2880
  if (typeof harness !== "object" || harness === null || Array.isArray(harness))
2865
2881
  throw new Error("config.yaml: harness must be a mapping of preset to harness (native | pi)");
@@ -2977,7 +2993,7 @@ function validateRuntimeOverrides(ro) {
2977
2993
  }
2978
2994
  }
2979
2995
  }
2980
- var MAX_INSTRUCTIONS_LENGTH, CONFIG_KEYS, SELF_IMPROVEMENT_KEYS, BOUNDARY_KEYS, MIN_BOUNDARY_MINUTES, ROUTE_ANSWER_MODES, ROUTING_KEYS, SHIP_KEYS, SPAWN_KEYS;
2996
+ var MAX_INSTRUCTIONS_LENGTH, CONFIG_KEYS, SELF_IMPROVEMENT_KEYS, BOUNDARY_KEYS, MIN_BOUNDARY_MINUTES, PI_KEYS, PI_COMPACTION_KEYS, ROUTE_ANSWER_MODES, ROUTING_KEYS, SHIP_KEYS, SPAWN_KEYS;
2981
2997
  var init_validate = __esm({
2982
2998
  "../../src/config/validate.ts"() {
2983
2999
  "use strict";
@@ -3012,6 +3028,7 @@ var init_validate = __esm({
3012
3028
  spawn: true,
3013
3029
  routing: true,
3014
3030
  harness: true,
3031
+ pi: true,
3015
3032
  slack: true,
3016
3033
  runHistory: true,
3017
3034
  runtimeOverrides: true,
@@ -3026,6 +3043,8 @@ var init_validate = __esm({
3026
3043
  };
3027
3044
  BOUNDARY_KEYS = { maxMinutes: true, maxIdentity: true, machines: true };
3028
3045
  MIN_BOUNDARY_MINUTES = 2;
3046
+ PI_KEYS = { compaction: true };
3047
+ PI_COMPACTION_KEYS = { reserveTokens: true, keepRecentTokens: true };
3029
3048
  ROUTE_ANSWER_MODES = ["tool", "text"];
3030
3049
  ROUTING_KEYS = { auto: true, model: true, answer: true };
3031
3050
  SHIP_KEYS = { maxRounds: true, maxMinutes: true };
@@ -11840,7 +11859,10 @@ function buildCostReport(group, cfg, usage, llm, range) {
11840
11859
  if (row.workflowName in workflowNames) workflowsUsd += usd;
11841
11860
  }
11842
11861
  byResource.workflows += workflowsUsd;
11843
- const llmUsd2 = llm && cfg.anthropicWorkspaceId ? llm.filter((r) => r.date === date && r.workspaceId === cfg.anthropicWorkspaceId).reduce((s, r) => s + r.amountUsd, 0) : 0;
11862
+ const llmRows = llm && cfg.anthropicWorkspaceId ? llm.filter((r) => r.date === date && r.workspaceId === cfg.anthropicWorkspaceId) : [];
11863
+ const llmUsd2 = llmRows.reduce((s, r) => s + r.amountUsd, 0);
11864
+ const llmEstimated = llmRows.some((r) => r.estimated === true);
11865
+ const llmUnpricedTokens = llmRows.reduce((s, r) => s + (r.unpricedTokens ?? 0), 0);
11844
11866
  const cloudUsd2 = Object.values(containers).reduce((s, c) => s + c.total, 0) + Object.values(durableObjects).reduce((s, v) => s + v, 0) + doRequestsUsd + doRowsUsd + doStorageUsd + workersUsd + r2Usd + workflowsUsd;
11845
11867
  return {
11846
11868
  date,
@@ -11854,6 +11876,8 @@ function buildCostReport(group, cfg, usage, llm, range) {
11854
11876
  workflowsUsd,
11855
11877
  cloudUsd: cloudUsd2,
11856
11878
  llmUsd: llmUsd2,
11879
+ llmEstimated,
11880
+ llmUnpricedTokens,
11857
11881
  total: cloudUsd2 + llmUsd2
11858
11882
  };
11859
11883
  });
@@ -11870,6 +11894,20 @@ function buildCostReport(group, cfg, usage, llm, range) {
11870
11894
  attribution
11871
11895
  };
11872
11896
  }
11897
+ function anthropicPriceOf(modelId) {
11898
+ const exact = ANTHROPIC_PRICES[modelId];
11899
+ if (exact) return exact;
11900
+ for (const family of Object.keys(ANTHROPIC_PRICES)) {
11901
+ if (modelId.startsWith(family) && DATED_RELEASE_SUFFIX.test(modelId.slice(family.length)))
11902
+ return ANTHROPIC_PRICES[family];
11903
+ }
11904
+ return void 0;
11905
+ }
11906
+ function anthropicTokensCostUsd(modelId, t) {
11907
+ const p = anthropicPriceOf(modelId);
11908
+ if (!p) return void 0;
11909
+ return (t.uncachedInput * p.input + t.output * p.output + t.cacheRead * p.cacheRead + t.cacheWrite5m * p.cacheWrite5m + t.cacheWrite1h * p.cacheWrite1h) / 1e6;
11910
+ }
11873
11911
  function resolveRange(daysParam, now = new Date(systemClock())) {
11874
11912
  const parsed = daysParam === null ? NaN : Number(daysParam);
11875
11913
  const days = Number.isInteger(parsed) ? Math.min(MAX_DAYS, Math.max(1, parsed)) : DEFAULT_DAYS;
@@ -11899,7 +11937,7 @@ function createCostsService(cfg, cloudflare, llm, now = () => new Date(systemClo
11899
11937
  }
11900
11938
  };
11901
11939
  }
11902
- var DEFAULT_CF_TOKEN_ENV, DEFAULT_ANTHROPIC_ADMIN_ENV, CLOUDFLARE_PRICES, GIB, GB, DAYS_PER_MONTH, TEMPLATE_BUCKET_NAMES, TEMPLATE_WORKFLOW_NAMES, emptySplit, DEFAULT_DAYS, MAX_DAYS, NullLlmCostSource, CF_GRAPHQL, CF_USAGE_QUERY, WORKFLOW_STEP_EVENTS, num2, str2, CloudflareGraphqlUsageSource, ANTHROPIC_COST_REPORT, ANTHROPIC_VERSION, MAX_COST_PAGES, AnthropicCostReportSource, COSTS_OFF_MESSAGE, NullCostsService;
11940
+ var DEFAULT_CF_TOKEN_ENV, DEFAULT_ANTHROPIC_ADMIN_ENV, CLOUDFLARE_PRICES, GIB, GB, DAYS_PER_MONTH, TEMPLATE_BUCKET_NAMES, TEMPLATE_WORKFLOW_NAMES, emptySplit, ANTHROPIC_PRICES, DATED_RELEASE_SUFFIX, DEFAULT_DAYS, MAX_DAYS, NullLlmCostSource, CF_GRAPHQL, CF_USAGE_QUERY, WORKFLOW_STEP_EVENTS, num2, str2, CloudflareGraphqlUsageSource, ANTHROPIC_COST_REPORT, ANTHROPIC_USAGE_REPORT, ANTHROPIC_VERSION, MAX_COST_PAGES, MAX_ESTIMATED_DAYS, AnthropicCostReportSource, COSTS_OFF_MESSAGE, NullCostsService;
11903
11941
  var init_costs = __esm({
11904
11942
  "../../src/core/costs.ts"() {
11905
11943
  "use strict";
@@ -11949,6 +11987,26 @@ var init_costs = __esm({
11949
11987
  r2: 0,
11950
11988
  workflows: 0
11951
11989
  });
11990
+ ANTHROPIC_PRICES = {
11991
+ "claude-fable-5-1": { input: 10, output: 50, cacheWrite5m: 12.5, cacheWrite1h: 20, cacheRead: 0.25 },
11992
+ "claude-mythos-5-1": { input: 10, output: 50, cacheWrite5m: 12.5, cacheWrite1h: 20, cacheRead: 0.25 },
11993
+ "claude-fable-5": { input: 10, output: 50, cacheWrite5m: 12.5, cacheWrite1h: 20, cacheRead: 1 },
11994
+ "claude-mythos-5": { input: 10, output: 50, cacheWrite5m: 12.5, cacheWrite1h: 20, cacheRead: 1 },
11995
+ "claude-opus-5": { input: 5, output: 25, cacheWrite5m: 6.25, cacheWrite1h: 10, cacheRead: 0.5 },
11996
+ "claude-opus-4-8": { input: 5, output: 25, cacheWrite5m: 6.25, cacheWrite1h: 10, cacheRead: 0.5 },
11997
+ "claude-opus-4-7": { input: 5, output: 25, cacheWrite5m: 6.25, cacheWrite1h: 10, cacheRead: 0.5 },
11998
+ "claude-opus-4-6": { input: 5, output: 25, cacheWrite5m: 6.25, cacheWrite1h: 10, cacheRead: 0.5 },
11999
+ "claude-opus-4-5": { input: 5, output: 25, cacheWrite5m: 6.25, cacheWrite1h: 10, cacheRead: 0.5 },
12000
+ "claude-opus-4-1": { input: 15, output: 75, cacheWrite5m: 18.75, cacheWrite1h: 30, cacheRead: 1.5 },
12001
+ "claude-opus-4": { input: 15, output: 75, cacheWrite5m: 18.75, cacheWrite1h: 30, cacheRead: 1.5 },
12002
+ "claude-sonnet-5": { input: 2, output: 10, cacheWrite5m: 2.5, cacheWrite1h: 4, cacheRead: 0.2 },
12003
+ "claude-sonnet-4-6": { input: 3, output: 15, cacheWrite5m: 3.75, cacheWrite1h: 6, cacheRead: 0.3 },
12004
+ "claude-sonnet-4-5": { input: 3, output: 15, cacheWrite5m: 3.75, cacheWrite1h: 6, cacheRead: 0.3 },
12005
+ "claude-sonnet-4": { input: 3, output: 15, cacheWrite5m: 3.75, cacheWrite1h: 6, cacheRead: 0.3 },
12006
+ "claude-haiku-4-5": { input: 1, output: 5, cacheWrite5m: 1.25, cacheWrite1h: 2, cacheRead: 0.1 },
12007
+ "claude-haiku-3-5": { input: 0.8, output: 4, cacheWrite5m: 1, cacheWrite1h: 1.6, cacheRead: 0.08 }
12008
+ };
12009
+ DATED_RELEASE_SUFFIX = /^-\d{8}$/;
11952
12010
  DEFAULT_DAYS = 30;
11953
12011
  MAX_DAYS = 90;
11954
12012
  NullLlmCostSource = class {
@@ -12062,8 +12120,10 @@ var init_costs = __esm({
12062
12120
  }
12063
12121
  };
12064
12122
  ANTHROPIC_COST_REPORT = "https://api.anthropic.com/v1/organizations/cost_report";
12123
+ ANTHROPIC_USAGE_REPORT = "https://api.anthropic.com/v1/organizations/usage_report/messages";
12065
12124
  ANTHROPIC_VERSION = "2023-06-01";
12066
12125
  MAX_COST_PAGES = 20;
12126
+ MAX_ESTIMATED_DAYS = 3;
12067
12127
  AnthropicCostReportSource = class {
12068
12128
  adminKey;
12069
12129
  fetchImpl;
@@ -12072,7 +12132,81 @@ var init_costs = __esm({
12072
12132
  this.fetchImpl = opts.fetchImpl ?? fetch;
12073
12133
  }
12074
12134
  async fetchDailyCost(range) {
12135
+ const report2 = await this.fetchCostReport(range);
12136
+ const open2 = eachDay(range).filter((date) => report2.closedThrough === null || date > report2.closedThrough);
12137
+ const estimated = await Promise.all(open2.slice(-MAX_ESTIMATED_DAYS).map((date) => this.estimateDay(date)));
12138
+ return [...report2.rows, ...estimated.flat()];
12139
+ }
12140
+ /** The hourly usage report for one day, summed per workspace × model and
12141
+ * priced at list; one row per workspace, `estimated`, with the tokens of
12142
+ * any model the price table does not know reported as `unpricedTokens`. */
12143
+ async estimateDay(date) {
12144
+ const perWorkspace = /* @__PURE__ */ new Map();
12145
+ let page2 = null;
12146
+ for (let i = 0; ; i++) {
12147
+ if (i >= MAX_COST_PAGES)
12148
+ throw new Error(
12149
+ `anthropic usage_report: more than ${MAX_COST_PAGES} pages for ${date}; refusing a truncated estimate`
12150
+ );
12151
+ const url = new URL(ANTHROPIC_USAGE_REPORT);
12152
+ url.searchParams.set("starting_at", `${date}T00:00:00Z`);
12153
+ url.searchParams.set("ending_at", `${addDays2(date, 1)}T00:00:00Z`);
12154
+ url.searchParams.set("bucket_width", "1h");
12155
+ url.searchParams.append("group_by[]", "workspace_id");
12156
+ url.searchParams.append("group_by[]", "model");
12157
+ url.searchParams.set("limit", "24");
12158
+ if (page2) url.searchParams.set("page", page2);
12159
+ const res = await this.fetchImpl(url.toString(), {
12160
+ headers: { "x-api-key": this.adminKey, "anthropic-version": ANTHROPIC_VERSION }
12161
+ });
12162
+ if (res.status !== 200)
12163
+ throw new Error(`anthropic usage_report ${res.status}: ${(await res.text()).slice(0, 300)}`);
12164
+ const body = await res.json();
12165
+ for (const bucket of body.data ?? []) {
12166
+ for (const r of bucket.results ?? []) {
12167
+ const ws = r.workspace_id ?? null;
12168
+ const byModel = perWorkspace.get(ws) ?? /* @__PURE__ */ new Map();
12169
+ const model = str2(r.model);
12170
+ const t = byModel.get(model) ?? {
12171
+ uncachedInput: 0,
12172
+ output: 0,
12173
+ cacheRead: 0,
12174
+ cacheWrite5m: 0,
12175
+ cacheWrite1h: 0
12176
+ };
12177
+ t.uncachedInput += num2(r.uncached_input_tokens);
12178
+ t.output += num2(r.output_tokens);
12179
+ t.cacheRead += num2(r.cache_read_input_tokens);
12180
+ t.cacheWrite5m += num2(r.cache_creation?.ephemeral_5m_input_tokens);
12181
+ t.cacheWrite1h += num2(r.cache_creation?.ephemeral_1h_input_tokens);
12182
+ byModel.set(model, t);
12183
+ perWorkspace.set(ws, byModel);
12184
+ }
12185
+ }
12186
+ if (!body.has_more || !body.next_page) break;
12187
+ page2 = body.next_page;
12188
+ }
12189
+ const out = [];
12190
+ for (const [workspaceId, byModel] of perWorkspace) {
12191
+ let amountUsd = 0;
12192
+ let unpricedTokens = 0;
12193
+ for (const [model, t] of byModel) {
12194
+ const usd = anthropicTokensCostUsd(model, t);
12195
+ if (usd === void 0)
12196
+ unpricedTokens += t.uncachedInput + t.output + t.cacheRead + t.cacheWrite5m + t.cacheWrite1h;
12197
+ else amountUsd += usd;
12198
+ }
12199
+ out.push({ date, workspaceId, amountUsd, estimated: true, unpricedTokens });
12200
+ }
12201
+ return out;
12202
+ }
12203
+ /** Every page of the cost report for the range, plus the last day it has a
12204
+ * bucket for (null when it has none in the range). */
12205
+ async fetchCostReport(range) {
12075
12206
  const rows = [];
12207
+ let closedThrough = null;
12208
+ const endExclusive = range.partialLastDay ? range.to : addDays2(range.to, 1);
12209
+ if (range.from >= endExclusive) return { rows, closedThrough };
12076
12210
  let page2 = null;
12077
12211
  for (let i = 0; ; i++) {
12078
12212
  if (i >= MAX_COST_PAGES)
@@ -12081,7 +12215,7 @@ var init_costs = __esm({
12081
12215
  );
12082
12216
  const url = new URL(ANTHROPIC_COST_REPORT);
12083
12217
  url.searchParams.set("starting_at", `${range.from}T00:00:00Z`);
12084
- url.searchParams.set("ending_at", `${addDays2(range.to, 1)}T00:00:00Z`);
12218
+ url.searchParams.set("ending_at", `${endExclusive}T00:00:00Z`);
12085
12219
  url.searchParams.set("bucket_width", "1d");
12086
12220
  url.searchParams.append("group_by[]", "workspace_id");
12087
12221
  url.searchParams.set("limit", "31");
@@ -12094,6 +12228,7 @@ var init_costs = __esm({
12094
12228
  const body = await res.json();
12095
12229
  for (const bucket of body.data ?? []) {
12096
12230
  const date = str2(bucket.starting_at).slice(0, 10);
12231
+ if (closedThrough === null || date > closedThrough) closedThrough = date;
12097
12232
  for (const r of bucket.results ?? []) {
12098
12233
  if (r.currency !== "USD") throw new Error(`anthropic cost_report: unexpected currency ${r.currency ?? "?"}`);
12099
12234
  const cents = Number(r.amount);
@@ -12104,7 +12239,7 @@ var init_costs = __esm({
12104
12239
  if (!body.has_more || !body.next_page) break;
12105
12240
  page2 = body.next_page;
12106
12241
  }
12107
- return rows;
12242
+ return { rows, closedThrough };
12108
12243
  }
12109
12244
  };
12110
12245
  COSTS_OFF_MESSAGE = "Cost reporting isn't configured \u2014 set costs.cloudflareAccountId + costs.groups in config and the CF_ANALYTICS_TOKEN secret to enable this view.";
@@ -30978,12 +31113,18 @@ function piSystemPrompt(spec) {
30978
31113
  ${harnessPromptNote(spec.relayTools, spec.identity)}
30979
31114
  `;
30980
31115
  }
30981
- function piSettingsJson() {
30982
- return JSON.stringify({ defaultProjectTrust: "never", checkForUpdates: false }, null, 2) + "\n";
31116
+ function piSettingsJson(compaction) {
31117
+ const settings = { defaultProjectTrust: "never", checkForUpdates: false };
31118
+ const thresholds = {
31119
+ ...compaction?.reserveTokens !== void 0 ? { reserveTokens: compaction.reserveTokens } : {},
31120
+ ...compaction?.keepRecentTokens !== void 0 ? { keepRecentTokens: compaction.keepRecentTokens } : {}
31121
+ };
31122
+ if (Object.keys(thresholds).length > 0) settings.compaction = thresholds;
31123
+ return JSON.stringify(settings, null, 2) + "\n";
30983
31124
  }
30984
31125
  function piLaunchFiles(spec) {
30985
31126
  return [
30986
- { path: `${spec.paths.agentDir}/settings.json`, content: piSettingsJson() },
31127
+ { path: `${spec.paths.agentDir}/settings.json`, content: piSettingsJson(spec.compaction) },
30987
31128
  { path: `${spec.paths.agentDir}/models.json`, content: piModelsJson(spec) },
30988
31129
  { path: `${spec.paths.agentDir}/SYSTEM.md`, content: piSystemPrompt(spec) },
30989
31130
  { path: spec.paths.extension, content: PI_EXTENSION_SOURCE }
@@ -32096,6 +32237,9 @@ function promptOf(seed) {
32096
32237
  }
32097
32238
  return { message: texts.join("\n\n"), ...images.length > 0 ? { images } : {} };
32098
32239
  }
32240
+ function settlementText(s) {
32241
+ return s.action === "synthetic" ? s.text : `The bot restarted while this ${s.toolUse.name} call was in flight; its result was lost \u2014 re-check its effects before re-running it.`;
32242
+ }
32099
32243
  function settlementResults(settlements) {
32100
32244
  if (settlements.length === 0) return void 0;
32101
32245
  return {
@@ -32103,11 +32247,14 @@ function settlementResults(settlements) {
32103
32247
  content: settlements.map((s) => ({
32104
32248
  type: "tool_result",
32105
32249
  toolUseId: s.toolUse.id,
32106
- content: s.action === "synthetic" ? s.text : `The bot restarted while this ${s.toolUse.name} call was in flight; its result was lost \u2014 re-check its effects before re-running it.`,
32250
+ content: settlementText(s),
32107
32251
  isError: true
32108
32252
  }))
32109
32253
  };
32110
32254
  }
32255
+ function settlementAnswer(s) {
32256
+ return { content: [{ type: "text", text: settlementText(s) }], isError: true };
32257
+ }
32111
32258
  async function runPiHarness(deps, run2) {
32112
32259
  const { container, clock: clock3 } = deps;
32113
32260
  const now = () => clock3();
@@ -32184,6 +32331,8 @@ async function runPiHarness(deps, run2) {
32184
32331
  }
32185
32332
  };
32186
32333
  const forget = deps.registry.register(live);
32334
+ const calls = deps.registry.calls(run2.runId);
32335
+ for (const s of run2.resume?.settlements ?? []) calls?.settle(s.toolUse.id, settlementAnswer(s));
32187
32336
  let pid;
32188
32337
  let transport;
32189
32338
  let facts2;
@@ -32228,9 +32377,10 @@ async function runPiHarness(deps, run2) {
32228
32377
  offset: recorded.logOffset
32229
32378
  });
32230
32379
  catchingUp = true;
32380
+ const inFlight = run2.resume?.settlements.length ?? 0;
32231
32381
  note(
32232
32382
  "resumed",
32233
- `resumed after a restart: pi still runs in the container (pid ${pid}); continuing its session with ${Math.round(remainingMs / 6e4)} min of budget left`
32383
+ `resumed after a restart: pi still runs in the container (pid ${pid}); continuing its session with ${Math.round(remainingMs / 6e4)} min of budget left` + (inFlight > 0 ? ` \u2014 ${inFlight} call(s) were in flight, each answered with a restart note if pi asks for it again` : "")
32234
32384
  );
32235
32385
  } else {
32236
32386
  paths = await container.makeRoot(run2.runId);
@@ -32245,7 +32395,8 @@ async function runPiHarness(deps, run2) {
32245
32395
  ...run2.effort ? { effort: run2.effort } : {},
32246
32396
  identity: run2.agent.identity,
32247
32397
  system: run2.system,
32248
- relayTools: run2.tools.map((t) => t.name)
32398
+ relayTools: run2.tools.map((t) => t.name),
32399
+ ...deps.compaction ? { compaction: deps.compaction } : {}
32249
32400
  };
32250
32401
  let session;
32251
32402
  if (run2.resume) {
@@ -32303,7 +32454,9 @@ async function runPiHarness(deps, run2) {
32303
32454
  }
32304
32455
  transport.send({ id: "retry", type: "set_auto_retry", enabled: false });
32305
32456
  transport.send({ id: "state", type: "get_state" });
32306
- if (reattached || run2.resume) transport.send({ id: "prompt", type: "prompt", message: CONTINUE_PROMPT });
32457
+ if (reattached)
32458
+ transport.send({ id: "prompt", type: "prompt", message: CONTINUE_PROMPT, streamingBehavior: "steer" });
32459
+ else if (run2.resume) transport.send({ id: "prompt", type: "prompt", message: CONTINUE_PROMPT });
32307
32460
  else transport.send({ id: "prompt", type: "prompt", ...promptOf(run2.messages) });
32308
32461
  let warned = false;
32309
32462
  let settled = false;
@@ -33457,6 +33610,9 @@ async function runLoop2(deps, ctx) {
33457
33610
  harnessUrl,
33458
33611
  registry: deps.harness.registry,
33459
33612
  ...deps.runBearers ? { bearers: deps.runBearers } : {},
33613
+ // The deployment's compaction thresholds ride into pi's settings for
33614
+ // every run on pi (harness-pi item 4); absent, pi's defaults stand.
33615
+ ...deps.config.config.pi?.compaction ? { compaction: deps.config.config.pi.compaction } : {},
33460
33616
  clock: clock3,
33461
33617
  sleep: (ms) => new Promise((r) => setTimeout(r, ms))
33462
33618
  },
@@ -42968,6 +43124,13 @@ var init_relay = __esm({
42968
43124
  }
42969
43125
  return answer;
42970
43126
  }
43127
+ /** The call's answer is known before it is asked (harness-pi item 8): the
43128
+ * record's settlement for a call in flight when the previous generation
43129
+ * died, whose extension asks again with the same id. Every ask reads it and
43130
+ * the tool never runs; a call already joined keeps the answer it has. */
43131
+ settle(callId, answer) {
43132
+ if (!this.calls.has(callId)) this.calls.set(callId, Promise.resolve(answer));
43133
+ }
42971
43134
  end() {
42972
43135
  this.ending.abort();
42973
43136
  this.calls.clear();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreplane/switchboard",
3
- "version": "1.221.1",
3
+ "version": "1.223.0",
4
4
  "description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://openswitchboard.dev",
@@ -1 +0,0 @@
1
- import{t as e}from"./AppShell-lYVcj-k7.js";import{Ci as t,Lr as n,Xt as r,_n as i,cn as a,gn as o,ir as s,ln as c,on as l,r as u,sn as d,un as f,ur as p,vi as m,wi as h,xi as g,yn as _}from"./seed-DeKSHrt2.js";var v=`Durable Objects`,y=`Workers · storage · R2`,b=`LLM (Anthropic)`,x=(e,t=2)=>`$${e.toFixed(t)}`,S=e=>e.workersUsd+e.doRowsUsd+e.doStorageUsd+e.r2Usd+e.workflowsUsd;function ee(e){let t=[];for(let n of e.days)for(let e of Object.keys(n.containers))t.includes(e)||t.push(e);return e.days.some(e=>Object.keys(e.durableObjects).length>0)&&t.push(v),e.days.some(e=>S(e)>0)&&t.push(y),e.llmAvailable&&t.push(b),t}function C(e,t){return t===`Durable Objects`?Object.values(e.durableObjects).reduce((e,t)=>e+t,0)+e.doRequestsUsd:t===`Workers · storage · R2`?S(e):t===`LLM (Anthropic)`?e.llmUsd:e.containers[t]?.total??0}function w(e){let t=e.range.partialLastDay?e.days.slice(0,-1):e.days,n=t[t.length-1],r=t.slice(-7),i=r.length?r.reduce((e,t)=>e+t.total,0)/r.length:0,a=e.totals.total>0?Math.round(e.totals.llmUsd/e.totals.total*100):0,o=e.account.cloudUsd>0?Math.round(e.totals.cloudUsd/e.account.cloudUsd*100):0;return{...n?{yesterday:{date:n.date,total:n.total}}:{},avg7:i,projectedMonth:i*30.4,llmShare:a,accountShare:o}}function T(e,t){let n={t:12,r:12,b:30,l:48},r=960-n.l-n.r,i=260-n.t-n.b,a=e.days,o=Math.max(1e-9,...a.map(e=>e.total))*1.08,s=r/Math.max(1,a.length),c=Math.min(10,s*.28),l=e=>n.t+i-e/o*i,u=Array.from({length:5},(e,t)=>{let n=o/4*t;return{y:Number(l(n).toFixed(1)),label:x(n)}}),d=[],f=[];return a.forEach((e,r)=>{let i=0,o=n.l+r*s+c/2,u=s-c;t.forEach((t,n)=>{let r=C(e,t);if(r<=0)return;let a=l(i+r),s=l(i),c=Math.max(0,s-a-2);d.push({seriesIndex:n,x:Number(o.toFixed(1)),y:Number(a.toFixed(1)),width:Number(u.toFixed(1)),height:Number(c.toFixed(1)),title:`${e.date} · ${t} · ${x(r)}`}),i+=r}),(a.length<=16||r%Math.ceil(a.length/16)===0)&&f.push({x:Number((o+u/2).toFixed(1)),label:e.date.slice(5)})}),{width:960,height:260,gridLines:u,segments:d,dayLabels:f,axisY:Number(l(0).toFixed(1)),marginLeft:n.l,marginRight:n.r}}function E(e){let t=e.totals.byResource,n=[[`Memory (provisioned while awake)`,t.memory],[`vCPU (active use only)`,t.cpu],[`Durable Object duration + requests`,t.durableObjects],[`Disk (provisioned while awake)`,t.disk],[`Workers requests + CPU`,t.workers],[`Durable Object SQLite rows`,t.doRows],[`Durable Object SQLite storage`,t.doStorage],[`R2 storage + operations`,t.r2],[`Workflow steps + state`,t.workflows]],r=n.reduce((e,t)=>e+t[1],0)||1;return n.map(([e,t])=>({label:e,usd:t,percent:t/r*100}))}var D=[`fill-[#4478b8] dark:fill-[#7aa7e0]`,`fill-[#c8823b] dark:fill-[#e0a95a]`,`fill-[#4c8c57] dark:fill-[#6fae7a]`,`fill-[#b8963e] dark:fill-[#d4b45a]`,`fill-[#b0568c] dark:fill-[#d489b8]`,`fill-[#6b7bcc] dark:fill-[#98a5e6]`,`fill-[#d05252] dark:fill-[#e07a74]`,`fill-[#3e8e96] dark:fill-[#6fb9c0]`],O=[`bg-[#4478b8] dark:bg-[#7aa7e0]`,`bg-[#c8823b] dark:bg-[#e0a95a]`,`bg-[#4c8c57] dark:bg-[#6fae7a]`,`bg-[#b8963e] dark:bg-[#d4b45a]`,`bg-[#b0568c] dark:bg-[#d489b8]`,`bg-[#6b7bcc] dark:bg-[#98a5e6]`,`bg-[#d05252] dark:bg-[#e07a74]`,`bg-[#3e8e96] dark:bg-[#6fb9c0]`],k={class:`overflow-x-auto`},A=[`viewBox`],j=[`x1`,`x2`,`y1`,`y2`],M=[`x`,`y`],N=[`x`,`y`,`width`,`height`],P=[`x`,`y`],F=[`x1`,`x2`,`y1`,`y2`],I=_({__name:`CostChart`,props:{report:{},series:{}},setup(e){let t=e,n=l(()=>T(t.report,t.series));return(e,t)=>(s(),f(`div`,k,[(s(),f(`svg`,{class:`block h-auto w-full font-mono`,viewBox:`0 0 ${n.value.width} ${n.value.height}`,role:`img`,"aria-label":`Daily cost, stacked by component`},[(s(!0),f(r,null,p(n.value.gridLines,(e,t)=>(s(),f(r,{key:`g${t}`},[d(`line`,{class:`stroke-(--ui-border-muted)`,x1:n.value.marginLeft,x2:n.value.width-n.value.marginRight,y1:e.y,y2:e.y},null,8,j),d(`text`,{class:`fill-(--ui-text-dimmed) text-[11px] tabular-nums`,x:n.value.marginLeft-8,y:e.y+4,"text-anchor":`end`},h(e.label),9,M)],64))),128)),(s(!0),f(r,null,p(n.value.segments,(e,t)=>(s(),f(`rect`,{key:`s${t}`,class:g([`seg hover:opacity-85`,m(D)[e.seriesIndex%m(D).length]]),x:e.x,y:e.y,width:e.width,height:e.height},[d(`title`,null,h(e.title),1)],10,N))),128)),(s(!0),f(r,null,p(n.value.dayLabels,(e,t)=>(s(),f(`text`,{key:`d${t}`,class:`fill-(--ui-text-dimmed) text-[11px]`,x:e.x,y:n.value.height-10,"text-anchor":`middle`},h(e.label),9,P))),128)),d(`line`,{class:`stroke-(--ui-border)`,x1:n.value.marginLeft,x2:n.value.width-n.value.marginRight,y1:n.value.axisY,y2:n.value.axisY},null,8,F)],8,A))]))}}),L={class:`text-xs text-muted`},R={key:1,class:`text-highlighted`},z=[`href`],B={class:`mb-4 flex flex-wrap items-center gap-x-4 gap-y-2`},V={key:0,class:`flex items-center gap-1`,"aria-label":`Cost groups`},H={key:0,class:`rounded-md bg-accented px-2.5 py-1 text-xs font-medium text-highlighted`,"aria-current":`page`},U=[`href`],W={class:`font-mono text-sm tabular-nums text-muted`},G={class:`mb-5 grid grid-cols-[repeat(auto-fit,minmax(190px,1fr))] gap-3`},K={class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},q={class:`font-mono text-2xl font-medium tabular-nums`},J={class:`text-xs text-muted`},Y={class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},X={class:`font-mono text-2xl font-medium tabular-nums`},Z={class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},Q={class:`font-mono text-2xl font-medium tabular-nums`},te={class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},ne={class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},re={class:`font-mono text-2xl font-medium tabular-nums`},ie={class:`text-xs text-muted`},ae={class:`grid gap-0.5 rounded-lg border border-default bg-elevated px-4 py-3.5`},oe={class:`font-mono text-2xl font-medium tabular-nums`},se={class:`text-xs text-muted`},ce={class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},le={class:`flex flex-wrap items-baseline justify-between gap-x-4 gap-y-2`},ue={class:`legend flex flex-wrap gap-x-4 gap-y-1.5 text-xs text-muted`},de={key:0,class:`text-xs text-warn`},fe={class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},pe={class:`w-full border-collapse font-mono text-[0.8125rem] tabular-nums`},me={class:`px-2.5 py-1.5`},he={class:`px-2.5 py-1.5 text-right`},ge={class:`px-2.5 py-1.5 text-right`},_e={class:`w-2/5 px-2.5 py-1.5`},ve={class:`mb-5 grid gap-3 rounded-lg border border-default bg-elevated px-5 py-4`},ye={open:``},be={class:`mt-2.5 overflow-x-auto`},$={class:`data w-full min-w-[38rem] border-collapse whitespace-nowrap font-mono text-[0.8125rem] tabular-nums`},xe=[`title`],Se={key:0,class:`text-xs text-dimmed`},Ce={class:`border-b border-muted px-2.5 py-1.5 text-right font-medium`},we={class:`text-xs text-muted`},Te={class:`rounded bg-accented px-1 py-0.5`},Ee={class:`border-t border-default pt-3.5 text-xs text-dimmed`},De={class:`mt-2 grid gap-1.5`},Oe=_({__name:`CostsPage`,setup(_){let v=u(`costs`),y=l(()=>v?.report??null),b=l(()=>v?.groups??[]),S=l(()=>y.value?ee(y.value):[]),T=l(()=>y.value?w(y.value):null),D=l(()=>y.value?E(y.value):[]),k=[7,30,90];function A(e){let t=new Date(`${e}T00:00:00Z`);return Number.isNaN(t.getTime())?e:`${[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`][t.getUTCMonth()]} ${t.getUTCDate()}`}return(l,u)=>y.value&&T.value?(s(),a(e,{key:0,title:`${y.value.label} spend`,nav:`costs`},{actions:n(()=>[d(`span`,L,[(s(),f(r,null,p(k,(e,t)=>(s(),f(r,{key:e},[t>0?(s(),f(r,{key:0},[o(` · `)],64)):c(``,!0),e===y.value.range.days?(s(),f(`b`,R,h(e)+`d`,1)):(s(),f(`a`,{key:2,class:`text-primary hover:underline`,href:`/costs/${y.value.group}?days=${e}`},h(e)+`d`,9,z))],64))),64))])]),default:n(()=>[d(`div`,B,[b.value.length>1?(s(),f(`nav`,V,[(s(!0),f(r,null,p(b.value,e=>(s(),f(r,{key:e},[e===y.value.group?(s(),f(`span`,H,h(e),1)):(s(),f(`a`,{key:1,class:`rounded-md px-2.5 py-1 text-xs text-muted no-underline hover:bg-elevated hover:text-highlighted`,href:`/costs/${e}`},h(e),9,U))],64))),128))])):c(``,!0),d(`p`,W,[o(h(A(y.value.range.from))+` → `+h(A(y.value.range.to))+` · `+h(y.value.range.days)+`d`,1),y.value.range.partialLastDay?(s(),f(r,{key:0},[o(` · today partial`)],64)):c(``,!0)])]),d(`section`,G,[d(`div`,K,[u[0]||=d(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`Yesterday`,-1),d(`span`,q,h(T.value.yesterday?m(x)(T.value.yesterday.total):`—`),1),d(`span`,J,h(T.value.yesterday?`${T.value.yesterday.date} · last full day`:`no full day in range`),1)]),d(`div`,Y,[u[1]||=d(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`7-day average`,-1),d(`span`,X,h(m(x)(T.value.avg7)),1),u[2]||=d(`span`,{class:`text-xs text-muted`},`per day, full days only`,-1)]),d(`div`,Z,[u[3]||=d(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`Projected month`,-1),d(`span`,Q,h(m(x)(T.value.projectedMonth,0)),1),u[4]||=d(`span`,{class:`text-xs text-muted`},`7-day rate × 30.4, before plan fees and included allowances`,-1)]),d(`div`,te,[d(`span`,ne,h(y.value.llmAvailable?`LLM share`:`LLM spend`),1),d(`span`,re,h(y.value.llmAvailable?`${T.value.llmShare}%`:`—`),1),d(`span`,ie,h(y.value.llmAvailable?`of the range total`:`LLM spend not configured`),1)]),d(`div`,ae,[u[5]||=d(`span`,{class:`font-mono text-[0.6875rem] font-medium uppercase tracking-widest text-dimmed`},`Share of account`,-1),d(`span`,oe,h(T.value.accountShare)+`%`,1),d(`span`,se,h(m(x)(y.value.totals.cloudUsd))+` of `+h(m(x)(y.value.account.cloudUsd))+` Cloudflare spend in range`,1)])]),d(`section`,ce,[d(`div`,le,[u[6]||=d(`div`,null,[d(`h2`,{class:`text-[0.9375rem] font-medium`},`Daily cost`),d(`p`,{class:`text-sm text-muted`},`Stacked by component · USD list price`)],-1),d(`div`,ue,[(s(!0),f(r,null,p(S.value,(e,t)=>(s(),f(`span`,{key:e,class:`inline-flex items-center gap-1.5`},[d(`i`,{class:g([`inline-block size-2.5 rounded-xs`,m(O)[t%m(O).length]])},null,2),o(h(e),1)]))),128))])]),i(I,{report:y.value,series:S.value},null,8,[`report`,`series`]),y.value.llmAvailable?c(``,!0):(s(),f(`p`,de,[...u[7]||=[o(` LLM spend not configured — set `,-1),d(`code`,null,`ANTHROPIC_ADMIN_KEY`,-1),o(` and the group's `,-1),d(`code`,null,`anthropicWorkspaceId`,-1),o(` to layer it in. `,-1)]]))]),d(`section`,fe,[u[8]||=d(`div`,null,[d(`h2`,{class:`text-[0.9375rem] font-medium`},`What each cloud dollar buys`)],-1),d(`table`,pe,[d(`tbody`,null,[(s(!0),f(r,null,p(D.value,e=>(s(),f(`tr`,{key:e.label,class:`border-b border-muted`},[d(`td`,me,h(e.label),1),d(`td`,he,h(m(x)(e.usd)),1),d(`td`,ge,h(Math.round(e.percent))+`%`,1),d(`td`,_e,[d(`i`,{class:`block h-2.5 rounded-xs bg-primary opacity-75`,style:t({width:`${e.percent.toFixed(1)}%`})},null,4)])]))),128))])])]),d(`section`,ve,[d(`details`,ye,[u[11]||=d(`summary`,{class:`cursor-pointer text-sm text-muted`},`Table view — daily cost by component (USD)`,-1),d(`div`,be,[d(`table`,$,[d(`thead`,null,[d(`tr`,null,[u[9]||=d(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-left text-xs font-medium text-muted`},` Date `,-1),(s(!0),f(r,null,p(S.value,e=>(s(),f(`th`,{key:e,class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-right text-xs font-medium text-muted`},h(e),1))),128)),u[10]||=d(`th`,{class:`border-b border-muted bg-(--ui-bg-muted) px-2.5 py-1.5 text-right text-xs font-medium text-muted`},` Total `,-1)])]),d(`tbody`,null,[(s(!0),f(r,null,p(y.value.days,e=>(s(),f(`tr`,{key:e.date},[d(`td`,{class:`border-b border-muted px-2.5 py-1.5`,title:e.date},[o(h(A(e.date))+` `,1),y.value.range.partialLastDay&&e.date===y.value.range.to?(s(),f(`span`,Se,`(partial day)`)):c(``,!0)],8,xe),(s(!0),f(r,null,p(S.value,t=>(s(),f(`td`,{key:t,class:`border-b border-muted px-2.5 py-1.5 text-right`},h(m(x)(m(C)(e,t),3)),1))),128)),d(`td`,Ce,h(m(x)(e.total,3)),1)]))),128))])])])]),d(`p`,we,[u[12]||=o(` Machine-readable twin: `,-1),d(`code`,Te,`GET /costs/`+h(y.value.group)+`.json`,1),u[13]||=o(` (same Access gate). `,-1)])]),d(`footer`,Ee,[d(`details`,null,[u[18]||=d(`summary`,{class:`cursor-pointer text-muted`},`How these numbers are computed`,-1),d(`div`,De,[u[16]||=d(`div`,null,[d(`b`,null,`Live.`),o(` Both billing sources are read live from this page — nothing cached, nothing stored. Cloudflare bills vCPU on active use only; memory and disk bill on the provisioned size for every second a container is awake. LLM spend is the Anthropic Admin API cost report for this group's workspace (gross, USD). `)],-1),u[17]||=d(`div`,null,[d(`b`,null,`Method.`),o(` Cloudflare GraphQL Analytics, every meter a Workers deployment is billed on: `),d(`code`,null,`containersUsageAdaptiveGroups`),o(` (cpuTimeSec, allocatedMemory, allocatedDisk per app per UTC day), `),d(`code`,null,`durableObjectsPeriodicGroups`),o(` (billable `),d(`code`,null,`duration`),o(` GB-s and SQLite rows read and written per namespace), `),d(`code`,null,`durableObjectsInvocationsAdaptiveGroups`),o(` (requests per namespace), `),d(`code`,null,`durableObjectsSqlStorageGroups`),o(` (bytes stored), `),d(`code`,null,`workersInvocationsAdaptive`),o(` (requests, CPU time), `),d(`code`,null,`r2StorageAdaptiveGroups`),o(` and `),d(`code`,null,`r2OperationsAdaptiveGroups`),o(` (bytes stored, class A/B operations). Prices: vCPU $0.000020/s, memory $0.0000025/GiB-s, disk $0.00000007/GB-s, DO duration $12.50 per million GB-s, DO requests $0.15/M, SQLite rows $0.001/M read and $1.00/M written, DO storage $0.20/GB-month, Workers $0.30/M requests and $0.02/M CPU-ms, R2 $0.015/GB-month, $4.50/M class A and $0.36/M class B (deletes free). Storage is the day's peak, prorated over a 30.44-day month. Gross list price — plan fees and included allowances are not subtracted; Workers Logs volume has no analytics dataset and is not priced. `)],-1),d(`div`,null,[u[14]||=d(`b`,null,`Scope.`,-1),u[15]||=o(` Attributed to this group: the Workers `,-1),d(`code`,null,h(y.value.attribution.workers.join(`, `)),1),o(`, every Durable Object namespace they host (`+h(Object.keys(y.value.attribution.durableObjectNamespaces).length)+`), the R2 buckets named after them (`+h(Object.keys(y.value.attribution.r2Buckets).length)+`), and the container apps mapped in `,1),d(`code`,null,`costs.groups.`+h(y.value.group),1),o(` (`+h(Object.keys(y.value.attribution.containerApps).length)+`). Everything else in the account is priced the same way into the account total, which is what "share of account" divides by. `,1)])])])])]),_:1},8,[`title`])):c(``,!0)}});export{Oe as default};