@apilift/cli 1.4.38 → 1.4.39

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.
@@ -349,9 +349,9 @@ var init_ledger = __esm({
349
349
  this.db.prepare("DELETE FROM usage_meta WHERE key='account_id'").run();
350
350
  }
351
351
  prune() {
352
- const expiredOutputs = this.db.prepare("SELECT output_path FROM invocation WHERE finished_at<? AND output_path IS NOT NULL").all(new Date(Date.now() - 90 * 864e5).toISOString());
352
+ const expiredOutputs = this.db.prepare("SELECT output_path FROM invocation WHERE finished_at<? AND (execution_id IS NULL OR status!='pending') AND output_path IS NOT NULL").all(new Date(Date.now() - 90 * 864e5).toISOString());
353
353
  for (const file2 of expiredOutputs) rmSync(file2.output_path, { force: true });
354
- this.db.prepare("DELETE FROM invocation WHERE finished_at<?").run(new Date(Date.now() - 90 * 864e5).toISOString());
354
+ this.db.prepare("DELETE FROM invocation WHERE finished_at<? AND (execution_id IS NULL OR status!='pending')").run(new Date(Date.now() - 90 * 864e5).toISOString());
355
355
  if (this.db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='baseline_candidate'").get()) {
356
356
  const expiredPayloads = this.db.prepare("SELECT payload_path FROM baseline_candidate WHERE started_at<? AND payload_path IS NOT NULL").all(new Date(Date.now() - 400 * 864e5).toISOString());
357
357
  for (const file2 of expiredPayloads) rmSync(file2.payload_path, { force: true });
@@ -28231,7 +28231,7 @@ var init_contracts = __esm({
28231
28231
  executionReportSchema = external_exports.strictObject({ id: uuid5, receiptId: uuid5.optional(), appId: uuid5, actionName: str, actionVersion: external_exports.number().int().positive(), closureHash: hash2, purpose: external_exports.enum(["run", "verify"]), clientFamily: external_exports.enum(["cli", "discovery"]), agent, outcome: external_exports.enum(["caller-error", "credential-error", "rate-limit", "app-outage", "interface-error", "uncertain", "network-error", "success", "cancelled"]), diagnostic: external_exports.enum(["operation-unavailable", "response-contract", "proof-failed"]).optional(), httpStatus: external_exports.number().int().optional(), proof: external_exports.enum(["passed", "failed", "unavailable"]).optional(), semanticConfirmed: external_exports.boolean().optional(), connectionFingerprint: hash2.optional(), startedAt: iso, dispatchedAt: iso.optional(), finishedAt: iso });
28232
28232
  sharedMeasure = { durationMs: external_exports.number().min(0).max(864e5).nullable(), tokens: external_exports.strictObject({ input: external_exports.number().min(0).max(1e8), output: external_exports.number().min(0).max(1e8) }).nullable(), coverage: external_exports.enum(["final", "partial", "unavailable"]), timeCoverage: external_exports.enum(["final", "partial", "unavailable"]), provider: external_exports.enum(["openai", "anthropic", "bedrock", "vertex", "custom"]).nullable(), model: external_exports.string().max(120).regex(/^[A-Za-z0-9._<>[\]:/@-]+$/).nullable() };
28233
28233
  executionMeasurementSchema = external_exports.strictObject({ id: uuid5, executionId: uuid5, revision: external_exports.number().int().positive(), purpose: external_exports.enum(["productive", "discovery", "verification", "setup", "unknown"]), session: hash2.nullable(), worker: hash2.nullable(), ...sharedMeasure });
28234
- baselineSampleSchema = external_exports.strictObject({ id: uuid5, revision: external_exports.number().int().positive(), appId: uuid5, actionName: str.nullable(), executableHash: hash2.nullable(), method: external_exports.enum(["browser", "direct"]).nullable(), completeness: external_exports.enum(["complete", "prefix"]), occurredAt: iso, ...sharedMeasure });
28234
+ baselineSampleSchema = external_exports.strictObject({ id: uuid5, revision: external_exports.number().int().positive(), appId: uuid5, actionName: str.nullable(), executableHash: hash2.nullable(), method: external_exports.enum(["browser", "direct"]).nullable(), publishedAction: external_exports.strictObject({ name: str, version: external_exports.number().int().positive() }).optional(), completeness: external_exports.enum(["complete", "prefix"]), occurredAt: iso, ...sharedMeasure });
28235
28235
  executionAcknowledgement = external_exports.strictObject({ results: external_exports.array(external_exports.strictObject({ id: uuid5, status: external_exports.enum(["accepted", "duplicate", "rejected"]), code: str.optional() })) });
28236
28236
  measurementAcknowledgement = external_exports.strictObject({ results: external_exports.array(external_exports.strictObject({ id: uuid5, revision: external_exports.number().int().positive(), status: external_exports.enum(["accepted", "duplicate", "pending", "rejected"]), code: str.optional() })) });
28237
28237
  noParams = empty;
@@ -28446,7 +28446,7 @@ var init_baselines = __esm({
28446
28446
  this.ledger = ledger;
28447
28447
  ledger.db.exec("CREATE TABLE IF NOT EXISTS baseline_candidate (id TEXT PRIMARY KEY, draft_id TEXT NOT NULL, app_id TEXT NOT NULL, session_id TEXT NOT NULL, method TEXT NOT NULL, action_name TEXT, executable_hash TEXT, started_at TEXT NOT NULL, stopped_at TEXT, duration_ms INTEGER, payload_path TEXT, tokens_input INTEGER, tokens_output INTEGER, coverage TEXT NOT NULL DEFAULT 'unavailable', time_coverage TEXT NOT NULL DEFAULT 'unavailable', completeness TEXT NOT NULL DEFAULT 'complete', measurement_json TEXT, status TEXT NOT NULL DEFAULT 'pending', attempts INTEGER NOT NULL DEFAULT 0, next_attempt_at INTEGER NOT NULL DEFAULT 0)");
28448
28448
  const columns = new Set(ledger.db.prepare("PRAGMA table_info(baseline_candidate)").all().map((c) => c.name));
28449
- for (const name of ["invocation_id", "http_started_at", "http_finished_at", "observation_finished_at", "observation_reason"]) if (!columns.has(name)) ledger.db.exec(`ALTER TABLE baseline_candidate ADD COLUMN ${name} TEXT`);
28449
+ for (const name of ["published_action", "invocation_id", "http_started_at", "http_finished_at", "observation_finished_at", "observation_reason"]) if (!columns.has(name)) ledger.db.exec(`ALTER TABLE baseline_candidate ADD COLUMN ${name} TEXT`);
28450
28450
  if (!columns.has("observation_version")) ledger.db.exec("ALTER TABLE baseline_candidate ADD COLUMN observation_version INTEGER");
28451
28451
  ledger.db.exec("CREATE INDEX IF NOT EXISTS baseline_delivery ON baseline_candidate (status,next_attempt_at,started_at)");
28452
28452
  ledger.db.exec("CREATE TABLE IF NOT EXISTS baseline_preparation_start (session_id TEXT NOT NULL, app_slug TEXT NOT NULL, started_at TEXT NOT NULL, PRIMARY KEY(session_id,app_slug))");
@@ -28485,9 +28485,13 @@ var init_baselines = __esm({
28485
28485
  if (input2.invocationId) this.ledger.db.prepare("UPDATE baseline_candidate SET invocation_id=?,http_started_at=?,http_finished_at=? WHERE id=?").run(input2.invocationId, input2.startedAt ?? null, input2.finishedAt ?? null, id);
28486
28486
  return id;
28487
28487
  }
28488
- published(draftId, actionName, executableHash, at = /* @__PURE__ */ new Date()) {
28488
+ published(draftId, actionName, executableHash, at = /* @__PURE__ */ new Date(), version2) {
28489
28489
  if (!/^[0-9a-f]{64}$/u.test(executableHash)) return;
28490
28490
  this.closeObservation(draftId, "published", at);
28491
+ if (version2) {
28492
+ const witness = JSON.stringify({ name: actionName, version: version2 });
28493
+ this.ledger.db.prepare("UPDATE baseline_candidate AS prep SET published_action=? WHERE prep.draft_id LIKE 'preparation:%' AND prep.published_action IS NULL AND EXISTS (SELECT 1 FROM baseline_candidate action WHERE action.draft_id=? AND action.session_id=prep.session_id AND action.app_id=prep.app_id AND action.method=prep.method AND action.stopped_at IS NOT NULL)").run(witness, draftId);
28494
+ }
28491
28495
  this.ledger.db.prepare("UPDATE baseline_candidate SET action_name=?,executable_hash=? WHERE draft_id=? AND stopped_at IS NOT NULL AND action_name IS NULL").run(actionName, executableHash, draftId);
28492
28496
  }
28493
28497
  rows() {
@@ -28495,8 +28499,8 @@ var init_baselines = __esm({
28495
28499
  }
28496
28500
  prepare(row, values) {
28497
28501
  const preparation = row.draft_id.startsWith("preparation:");
28498
- if (!preparation && (!row.action_name || !row.executable_hash) || !row.stopped_at || row.observation_version === 1 && !row.observation_finished_at) return;
28499
- const payload = { id: row.id, revision: 1, appId: row.app_id, actionName: preparation ? null : row.action_name, executableHash: preparation ? null : row.executable_hash, method: preparation ? row.method : null, completeness: values.completeness, occurredAt: row.stopped_at, durationMs: values.durationMs, tokens: values.input !== null && values.output !== null ? { input: values.input, output: values.output } : null, coverage: values.coverage, timeCoverage: values.timeCoverage, provider: values.provider ?? null, model: values.model ?? null };
28502
+ if (preparation && !row.published_action || !preparation && (!row.action_name || !row.executable_hash) || !row.stopped_at || row.observation_version === 1 && !row.observation_finished_at) return;
28503
+ const payload = { id: row.id, revision: 1, appId: row.app_id, actionName: preparation ? null : row.action_name, executableHash: preparation ? null : row.executable_hash, method: preparation ? row.method : null, ...preparation && row.published_action ? { publishedAction: JSON.parse(row.published_action) } : {}, completeness: values.completeness, occurredAt: row.stopped_at, durationMs: values.durationMs, tokens: values.input !== null && values.output !== null ? { input: values.input, output: values.output } : null, coverage: values.coverage, timeCoverage: values.timeCoverage, provider: values.provider ?? null, model: values.model ?? null };
28500
28504
  const checked = baselineSampleSchema.safeParse(payload);
28501
28505
  if (!checked.success) {
28502
28506
  this.ledger.db.prepare("UPDATE baseline_candidate SET status='rejected' WHERE id=?").run(row.id);
@@ -163723,7 +163727,7 @@ function reconcile(original, facts) {
163723
163727
  return { durationMs: elapsed ?? fallback.durationMs, input: input2, output: output2, coverage: tool.resultTokens === null && row.input_tokens === null ? "unavailable" : gap || tool.mediaUnknown ? "partial" : "final", timeCoverage: gap ? "partial" : "final", provider: request.provider, model: request.model, costMicrousd: price(input2, output2, request.provider, request.model, new Date(tool.returnedAt ?? finish)), linked: true, trace: { start: tool.calledAt ?? start, finish: tool.returnedAt ?? finish, boundarySource: elapsed === null ? "cli" : "native-tool", contributions: [part], countedIntervals: [{ start: tool.calledAt ?? start, finish: tool.returnedAt ?? finish }], excludedIntervals: [], notes: [] } };
163724
163728
  }
163725
163729
  function baselineEligible(row) {
163726
- return Boolean(row.stopped_at && (row.observation_version !== 1 || row.observation_finished_at) && (row.draft_id.startsWith("preparation:") || row.action_name && row.executable_hash));
163730
+ return Boolean(row.stopped_at && (row.observation_version !== 1 || row.observation_finished_at) && (row.draft_id.startsWith("preparation:") ? row.published_action : row.action_name && row.executable_hash));
163727
163731
  }
163728
163732
  function observationBoundary(row, facts, invocations, baselines) {
163729
163733
  const start = Date.parse(row.started_at);
@@ -163908,24 +163912,10 @@ __export(collector_exports, {
163908
163912
  largestRemainder: () => largestRemainder,
163909
163913
  reconcile: () => reconcile,
163910
163914
  reconcileBaselines: () => reconcileBaselines,
163911
- reconcilePending: () => reconcilePending,
163912
- upgradeMeasurementAdapter: () => upgradeMeasurementAdapter
163915
+ reconcilePending: () => reconcilePending
163913
163916
  });
163914
163917
  import { rmSync as rmSync2, statSync as statSync3 } from "node:fs";
163915
163918
  import { createHash as createHash4 } from "node:crypto";
163916
- function upgradeMeasurementAdapter(ledger) {
163917
- const previous = ledger.db.prepare("SELECT value FROM usage_meta WHERE key='measurement_adapter_revision'").get();
163918
- if (previous && Number(previous.value) >= ADAPTER_REVISION) return;
163919
- ledger.db.exec("BEGIN IMMEDIATE");
163920
- try {
163921
- ledger.db.prepare("UPDATE invocation SET revision=revision+1,measurement_json=NULL,status='pending',attempts=0,next_attempt_at=0 WHERE execution_id IS NOT NULL AND finished_at>=? AND measurement_json IS NOT NULL AND (session_id LIKE '%:label:%' OR json_extract(measurement_json,'$.coverage')='unavailable' OR json_extract(measurement_json,'$.timeCoverage')='unavailable' OR json_extract(measurement_json,'$.provider') IS NULL OR json_extract(measurement_json,'$.model') IS NULL)").run(new Date(Date.now() - 7 * 864e5).toISOString());
163922
- ledger.db.prepare("INSERT INTO usage_meta(key,value) VALUES ('measurement_adapter_revision',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value").run(String(ADAPTER_REVISION));
163923
- ledger.db.exec("COMMIT");
163924
- } catch (error62) {
163925
- ledger.db.exec("ROLLBACK");
163926
- throw error62;
163927
- }
163928
- }
163929
163919
  function hasPendingUsage(ledger) {
163930
163920
  if (ledger.db.prepare("SELECT count(*) AS n FROM invocation WHERE output_path IS NOT NULL OR (execution_id IS NOT NULL AND status='pending')").get().n) return true;
163931
163921
  if (!ledger.db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='baseline_candidate'").get()) return false;
@@ -163960,15 +163950,28 @@ function sourcePaths(agent2, sessionId, env) {
163960
163950
  const paths = transcriptPaths(agent2, sessionId, env);
163961
163951
  return paths.length || !sessionId.includes(":label:") ? paths : transcriptPaths(agent2, "", env);
163962
163952
  }
163963
- function reconcilePending(ledger, env, now = Date.now(), scan = new TranscriptScan(ledger)) {
163964
- const rows = ledger.db.prepare("SELECT * FROM invocation WHERE execution_id IS NOT NULL AND measurement_json IS NULL AND output_path IS NULL ORDER BY finished_at LIMIT 100").all();
163953
+ function reconcilePending(ledger, env, scan = new TranscriptScan(ledger)) {
163954
+ const rows = ledger.db.prepare("SELECT * FROM invocation WHERE execution_id IS NOT NULL AND measurement_json IS NULL AND output_path IS NULL ORDER BY finished_at").iterate();
163965
163955
  for (const row of rows) {
163966
163956
  const facts = factsFor(row, env, scan);
163967
- if (!facts && (scan.deferred || now - Date.parse(row.finished_at) < THIRTY_MINUTES)) continue;
163957
+ if (!facts) continue;
163958
+ const origins = invocationMatches(row, facts);
163959
+ if (!origins.length || facts.pendingTail || origins.some(({ request, tool }) => !request.final || tool.returnedAt === null)) continue;
163968
163960
  const actual = reconcile(row, facts);
163969
- const purpose = row.command_key === "run" && row.session_id ? "productive" : row.purpose;
163970
- const identity = actual.linked && facts?.sessionId ? `${row.agent}:${facts.sessionId}` : row.session_id;
163961
+ const purpose = !actual.linked ? "unknown" : row.command_key === "run" && row.session_id ? "productive" : row.purpose;
163962
+ const identity = actual.linked && facts.sessionId ? `${row.agent}:${facts.sessionId}` : row.session_id;
163971
163963
  const payload = { id: row.id, executionId: row.execution_id, revision: row.revision, purpose, session: ledger.pseudonym(row.agent, identity), worker: ledger.pseudonym(row.agent, identity), durationMs: actual.durationMs, tokens: actual.input !== null && actual.output !== null ? { input: actual.input, output: actual.output } : null, coverage: actual.coverage, timeCoverage: actual.timeCoverage, provider: actual.provider, model: actual.model };
163964
+ const shape = executionMeasurementSchema.shape;
163965
+ if (!shape.durationMs.safeParse(payload.durationMs).success) {
163966
+ payload.durationMs = null;
163967
+ payload.timeCoverage = "unavailable";
163968
+ }
163969
+ if (!shape.tokens.safeParse(payload.tokens).success) {
163970
+ payload.tokens = null;
163971
+ payload.coverage = "unavailable";
163972
+ }
163973
+ if (!shape.provider.safeParse(payload.provider).success) payload.provider = null;
163974
+ if (!shape.model.safeParse(payload.model).success) payload.model = null;
163972
163975
  const checked = executionMeasurementSchema.safeParse(payload);
163973
163976
  if (checked.success) ledger.db.prepare("UPDATE invocation SET measurement_json=?,status='pending' WHERE id=?").run(JSON.stringify(checked.data), row.id);
163974
163977
  else ledger.db.prepare("UPDATE invocation SET status='rejected' WHERE id=?").run(row.id);
@@ -164005,7 +164008,6 @@ async function deliverPending(ledger, state, site, fetcher = fetch, now = Date.n
164005
164008
  const installation = state.installation();
164006
164009
  if (!installation) return;
164007
164010
  const cutoff = new Date(now - 7 * 864e5).toISOString();
164008
- ledger.db.prepare("UPDATE invocation SET status='rejected' WHERE status='pending' AND finished_at<?").run(cutoff);
164009
164011
  ledger.db.prepare("UPDATE baseline_candidate SET status='rejected' WHERE status='pending' AND stopped_at IS NOT NULL AND stopped_at<?").run(cutoff);
164010
164012
  const rows = ledger.pendingMeasurement(now);
164011
164013
  const baselines = new BaselineStore(ledger);
@@ -164053,10 +164055,9 @@ async function collectOnce(dataRoot2, env, fetcher = fetch) {
164053
164055
  if (!installation) return;
164054
164056
  await flushExecutionReports(createCliApi(new URL(env.APILIFT_URL || "https://apilift.dev").origin, fetcher, installation.token), state);
164055
164057
  ledger.clearForAccountChange(installation.accountId);
164056
- upgradeMeasurementAdapter(ledger);
164057
164058
  countPending(ledger);
164058
164059
  const scan = new TranscriptScan(ledger);
164059
- reconcilePending(ledger, env, Date.now(), scan);
164060
+ reconcilePending(ledger, env, scan);
164060
164061
  reconcileBaselines(ledger, env, Date.now(), scan);
164061
164062
  await deliverPending(ledger, state, new URL(env.APILIFT_URL || "https://apilift.dev").origin, fetcher);
164062
164063
  ledger.prune();
@@ -164065,7 +164066,7 @@ async function collectOnce(dataRoot2, env, fetcher = fetch) {
164065
164066
  state.close();
164066
164067
  }
164067
164068
  }
164068
- var THIRTY_MINUTES, ADAPTER_REVISION, scans, TranscriptScan;
164069
+ var THIRTY_MINUTES, scans, TranscriptScan;
164069
164070
  var init_collector = __esm({
164070
164071
  "src/usage/collector.ts"() {
164071
164072
  "use strict";
@@ -164080,7 +164081,6 @@ var init_collector = __esm({
164080
164081
  init_measurement();
164081
164082
  init_ownership();
164082
164083
  THIRTY_MINUTES = 30 * 6e4;
164083
- ADAPTER_REVISION = 3;
164084
164084
  scans = /* @__PURE__ */ new Map();
164085
164085
  TranscriptScan = class {
164086
164086
  constructor(ledger) {
@@ -29704,7 +29704,7 @@ var billing = external_exports.strictObject({ workspaceId: uuid3, paddleCustomer
29704
29704
  var executionReportSchema = external_exports.strictObject({ id: uuid3, receiptId: uuid3.optional(), appId: uuid3, actionName: str, actionVersion: external_exports.number().int().positive(), closureHash: hash3, purpose: external_exports.enum(["run", "verify"]), clientFamily: external_exports.enum(["cli", "discovery"]), agent, outcome: external_exports.enum(["caller-error", "credential-error", "rate-limit", "app-outage", "interface-error", "uncertain", "network-error", "success", "cancelled"]), diagnostic: external_exports.enum(["operation-unavailable", "response-contract", "proof-failed"]).optional(), httpStatus: external_exports.number().int().optional(), proof: external_exports.enum(["passed", "failed", "unavailable"]).optional(), semanticConfirmed: external_exports.boolean().optional(), connectionFingerprint: hash3.optional(), startedAt: iso, dispatchedAt: iso.optional(), finishedAt: iso });
29705
29705
  var sharedMeasure = { durationMs: external_exports.number().min(0).max(864e5).nullable(), tokens: external_exports.strictObject({ input: external_exports.number().min(0).max(1e8), output: external_exports.number().min(0).max(1e8) }).nullable(), coverage: external_exports.enum(["final", "partial", "unavailable"]), timeCoverage: external_exports.enum(["final", "partial", "unavailable"]), provider: external_exports.enum(["openai", "anthropic", "bedrock", "vertex", "custom"]).nullable(), model: external_exports.string().max(120).regex(/^[A-Za-z0-9._<>[\]:/@-]+$/).nullable() };
29706
29706
  var executionMeasurementSchema = external_exports.strictObject({ id: uuid3, executionId: uuid3, revision: external_exports.number().int().positive(), purpose: external_exports.enum(["productive", "discovery", "verification", "setup", "unknown"]), session: hash3.nullable(), worker: hash3.nullable(), ...sharedMeasure });
29707
- var baselineSampleSchema = external_exports.strictObject({ id: uuid3, revision: external_exports.number().int().positive(), appId: uuid3, actionName: str.nullable(), executableHash: hash3.nullable(), method: external_exports.enum(["browser", "direct"]).nullable(), completeness: external_exports.enum(["complete", "prefix"]), occurredAt: iso, ...sharedMeasure });
29707
+ var baselineSampleSchema = external_exports.strictObject({ id: uuid3, revision: external_exports.number().int().positive(), appId: uuid3, actionName: str.nullable(), executableHash: hash3.nullable(), method: external_exports.enum(["browser", "direct"]).nullable(), publishedAction: external_exports.strictObject({ name: str, version: external_exports.number().int().positive() }).optional(), completeness: external_exports.enum(["complete", "prefix"]), occurredAt: iso, ...sharedMeasure });
29708
29708
  var executionAcknowledgement = external_exports.strictObject({ results: external_exports.array(external_exports.strictObject({ id: uuid3, status: external_exports.enum(["accepted", "duplicate", "rejected"]), code: str.optional() })) });
29709
29709
  var measurementAcknowledgement = external_exports.strictObject({ results: external_exports.array(external_exports.strictObject({ id: uuid3, revision: external_exports.number().int().positive(), status: external_exports.enum(["accepted", "duplicate", "pending", "rejected"]), code: str.optional() })) });
29710
29710
  function apiContract(method, path2, auth, params, query, body, response, errors = []) {
@@ -154748,7 +154748,7 @@ function reconcile(original, facts) {
154748
154748
  return { durationMs: elapsed ?? fallback.durationMs, input: input2, output: output2, coverage: tool.resultTokens === null && row.input_tokens === null ? "unavailable" : gap || tool.mediaUnknown ? "partial" : "final", timeCoverage: gap ? "partial" : "final", provider: request.provider, model: request.model, costMicrousd: price(input2, output2, request.provider, request.model, new Date(tool.returnedAt ?? finish)), linked: true, trace: { start: tool.calledAt ?? start, finish: tool.returnedAt ?? finish, boundarySource: elapsed === null ? "cli" : "native-tool", contributions: [part], countedIntervals: [{ start: tool.calledAt ?? start, finish: tool.returnedAt ?? finish }], excludedIntervals: [], notes: [] } };
154749
154749
  }
154750
154750
  function baselineEligible(row) {
154751
- return Boolean(row.stopped_at && (row.observation_version !== 1 || row.observation_finished_at) && (row.draft_id.startsWith("preparation:") || row.action_name && row.executable_hash));
154751
+ return Boolean(row.stopped_at && (row.observation_version !== 1 || row.observation_finished_at) && (row.draft_id.startsWith("preparation:") ? row.published_action : row.action_name && row.executable_hash));
154752
154752
  }
154753
154753
  function observationBoundary(row, facts, invocations, baselines) {
154754
154754
  const start = Date.parse(row.started_at);
package/dist/apilift.js CHANGED
@@ -28687,7 +28687,7 @@ var init_contracts = __esm({
28687
28687
  executionReportSchema = external_exports.strictObject({ id: uuid3, receiptId: uuid3.optional(), appId: uuid3, actionName: str, actionVersion: external_exports.number().int().positive(), closureHash: hash3, purpose: external_exports.enum(["run", "verify"]), clientFamily: external_exports.enum(["cli", "discovery"]), agent, outcome: external_exports.enum(["caller-error", "credential-error", "rate-limit", "app-outage", "interface-error", "uncertain", "network-error", "success", "cancelled"]), diagnostic: external_exports.enum(["operation-unavailable", "response-contract", "proof-failed"]).optional(), httpStatus: external_exports.number().int().optional(), proof: external_exports.enum(["passed", "failed", "unavailable"]).optional(), semanticConfirmed: external_exports.boolean().optional(), connectionFingerprint: hash3.optional(), startedAt: iso, dispatchedAt: iso.optional(), finishedAt: iso });
28688
28688
  sharedMeasure = { durationMs: external_exports.number().min(0).max(864e5).nullable(), tokens: external_exports.strictObject({ input: external_exports.number().min(0).max(1e8), output: external_exports.number().min(0).max(1e8) }).nullable(), coverage: external_exports.enum(["final", "partial", "unavailable"]), timeCoverage: external_exports.enum(["final", "partial", "unavailable"]), provider: external_exports.enum(["openai", "anthropic", "bedrock", "vertex", "custom"]).nullable(), model: external_exports.string().max(120).regex(/^[A-Za-z0-9._<>[\]:/@-]+$/).nullable() };
28689
28689
  executionMeasurementSchema = external_exports.strictObject({ id: uuid3, executionId: uuid3, revision: external_exports.number().int().positive(), purpose: external_exports.enum(["productive", "discovery", "verification", "setup", "unknown"]), session: hash3.nullable(), worker: hash3.nullable(), ...sharedMeasure });
28690
- baselineSampleSchema = external_exports.strictObject({ id: uuid3, revision: external_exports.number().int().positive(), appId: uuid3, actionName: str.nullable(), executableHash: hash3.nullable(), method: external_exports.enum(["browser", "direct"]).nullable(), completeness: external_exports.enum(["complete", "prefix"]), occurredAt: iso, ...sharedMeasure });
28690
+ baselineSampleSchema = external_exports.strictObject({ id: uuid3, revision: external_exports.number().int().positive(), appId: uuid3, actionName: str.nullable(), executableHash: hash3.nullable(), method: external_exports.enum(["browser", "direct"]).nullable(), publishedAction: external_exports.strictObject({ name: str, version: external_exports.number().int().positive() }).optional(), completeness: external_exports.enum(["complete", "prefix"]), occurredAt: iso, ...sharedMeasure });
28691
28691
  executionAcknowledgement = external_exports.strictObject({ results: external_exports.array(external_exports.strictObject({ id: uuid3, status: external_exports.enum(["accepted", "duplicate", "rejected"]), code: str.optional() })) });
28692
28692
  measurementAcknowledgement = external_exports.strictObject({ results: external_exports.array(external_exports.strictObject({ id: uuid3, revision: external_exports.number().int().positive(), status: external_exports.enum(["accepted", "duplicate", "pending", "rejected"]), code: str.optional() })) });
28693
28693
  noParams = empty2;
@@ -29179,9 +29179,9 @@ var init_ledger = __esm({
29179
29179
  this.db.prepare("DELETE FROM usage_meta WHERE key='account_id'").run();
29180
29180
  }
29181
29181
  prune() {
29182
- const expiredOutputs = this.db.prepare("SELECT output_path FROM invocation WHERE finished_at<? AND output_path IS NOT NULL").all(new Date(Date.now() - 90 * 864e5).toISOString());
29182
+ const expiredOutputs = this.db.prepare("SELECT output_path FROM invocation WHERE finished_at<? AND (execution_id IS NULL OR status!='pending') AND output_path IS NOT NULL").all(new Date(Date.now() - 90 * 864e5).toISOString());
29183
29183
  for (const file2 of expiredOutputs) rmSync2(file2.output_path, { force: true });
29184
- this.db.prepare("DELETE FROM invocation WHERE finished_at<?").run(new Date(Date.now() - 90 * 864e5).toISOString());
29184
+ this.db.prepare("DELETE FROM invocation WHERE finished_at<? AND (execution_id IS NULL OR status!='pending')").run(new Date(Date.now() - 90 * 864e5).toISOString());
29185
29185
  if (this.db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='baseline_candidate'").get()) {
29186
29186
  const expiredPayloads = this.db.prepare("SELECT payload_path FROM baseline_candidate WHERE started_at<? AND payload_path IS NOT NULL").all(new Date(Date.now() - 400 * 864e5).toISOString());
29187
29187
  for (const file2 of expiredPayloads) rmSync2(file2.payload_path, { force: true });
@@ -29222,7 +29222,7 @@ var init_baselines = __esm({
29222
29222
  this.ledger = ledger;
29223
29223
  ledger.db.exec("CREATE TABLE IF NOT EXISTS baseline_candidate (id TEXT PRIMARY KEY, draft_id TEXT NOT NULL, app_id TEXT NOT NULL, session_id TEXT NOT NULL, method TEXT NOT NULL, action_name TEXT, executable_hash TEXT, started_at TEXT NOT NULL, stopped_at TEXT, duration_ms INTEGER, payload_path TEXT, tokens_input INTEGER, tokens_output INTEGER, coverage TEXT NOT NULL DEFAULT 'unavailable', time_coverage TEXT NOT NULL DEFAULT 'unavailable', completeness TEXT NOT NULL DEFAULT 'complete', measurement_json TEXT, status TEXT NOT NULL DEFAULT 'pending', attempts INTEGER NOT NULL DEFAULT 0, next_attempt_at INTEGER NOT NULL DEFAULT 0)");
29224
29224
  const columns = new Set(ledger.db.prepare("PRAGMA table_info(baseline_candidate)").all().map((c) => c.name));
29225
- for (const name of ["invocation_id", "http_started_at", "http_finished_at", "observation_finished_at", "observation_reason"]) if (!columns.has(name)) ledger.db.exec(`ALTER TABLE baseline_candidate ADD COLUMN ${name} TEXT`);
29225
+ for (const name of ["published_action", "invocation_id", "http_started_at", "http_finished_at", "observation_finished_at", "observation_reason"]) if (!columns.has(name)) ledger.db.exec(`ALTER TABLE baseline_candidate ADD COLUMN ${name} TEXT`);
29226
29226
  if (!columns.has("observation_version")) ledger.db.exec("ALTER TABLE baseline_candidate ADD COLUMN observation_version INTEGER");
29227
29227
  ledger.db.exec("CREATE INDEX IF NOT EXISTS baseline_delivery ON baseline_candidate (status,next_attempt_at,started_at)");
29228
29228
  ledger.db.exec("CREATE TABLE IF NOT EXISTS baseline_preparation_start (session_id TEXT NOT NULL, app_slug TEXT NOT NULL, started_at TEXT NOT NULL, PRIMARY KEY(session_id,app_slug))");
@@ -29261,9 +29261,13 @@ var init_baselines = __esm({
29261
29261
  if (input2.invocationId) this.ledger.db.prepare("UPDATE baseline_candidate SET invocation_id=?,http_started_at=?,http_finished_at=? WHERE id=?").run(input2.invocationId, input2.startedAt ?? null, input2.finishedAt ?? null, id2);
29262
29262
  return id2;
29263
29263
  }
29264
- published(draftId, actionName, executableHash2, at = /* @__PURE__ */ new Date()) {
29264
+ published(draftId, actionName, executableHash2, at = /* @__PURE__ */ new Date(), version2) {
29265
29265
  if (!/^[0-9a-f]{64}$/u.test(executableHash2)) return;
29266
29266
  this.closeObservation(draftId, "published", at);
29267
+ if (version2) {
29268
+ const witness = JSON.stringify({ name: actionName, version: version2 });
29269
+ this.ledger.db.prepare("UPDATE baseline_candidate AS prep SET published_action=? WHERE prep.draft_id LIKE 'preparation:%' AND prep.published_action IS NULL AND EXISTS (SELECT 1 FROM baseline_candidate action WHERE action.draft_id=? AND action.session_id=prep.session_id AND action.app_id=prep.app_id AND action.method=prep.method AND action.stopped_at IS NOT NULL)").run(witness, draftId);
29270
+ }
29267
29271
  this.ledger.db.prepare("UPDATE baseline_candidate SET action_name=?,executable_hash=? WHERE draft_id=? AND stopped_at IS NOT NULL AND action_name IS NULL").run(actionName, executableHash2, draftId);
29268
29272
  }
29269
29273
  rows() {
@@ -29271,8 +29275,8 @@ var init_baselines = __esm({
29271
29275
  }
29272
29276
  prepare(row, values) {
29273
29277
  const preparation = row.draft_id.startsWith("preparation:");
29274
- if (!preparation && (!row.action_name || !row.executable_hash) || !row.stopped_at || row.observation_version === 1 && !row.observation_finished_at) return;
29275
- const payload = { id: row.id, revision: 1, appId: row.app_id, actionName: preparation ? null : row.action_name, executableHash: preparation ? null : row.executable_hash, method: preparation ? row.method : null, completeness: values.completeness, occurredAt: row.stopped_at, durationMs: values.durationMs, tokens: values.input !== null && values.output !== null ? { input: values.input, output: values.output } : null, coverage: values.coverage, timeCoverage: values.timeCoverage, provider: values.provider ?? null, model: values.model ?? null };
29278
+ if (preparation && !row.published_action || !preparation && (!row.action_name || !row.executable_hash) || !row.stopped_at || row.observation_version === 1 && !row.observation_finished_at) return;
29279
+ const payload = { id: row.id, revision: 1, appId: row.app_id, actionName: preparation ? null : row.action_name, executableHash: preparation ? null : row.executable_hash, method: preparation ? row.method : null, ...preparation && row.published_action ? { publishedAction: JSON.parse(row.published_action) } : {}, completeness: values.completeness, occurredAt: row.stopped_at, durationMs: values.durationMs, tokens: values.input !== null && values.output !== null ? { input: values.input, output: values.output } : null, coverage: values.coverage, timeCoverage: values.timeCoverage, provider: values.provider ?? null, model: values.model ?? null };
29276
29280
  const checked = baselineSampleSchema.safeParse(payload);
29277
29281
  if (!checked.success) {
29278
29282
  this.ledger.db.prepare("UPDATE baseline_candidate SET status='rejected' WHERE id=?").run(row.id);
@@ -34530,7 +34534,7 @@ If no usable public route is found, record the evidence and use browser transpor
34530
34534
  return publicationError(error62);
34531
34535
  }
34532
34536
  store.published(session.id, published.resource.version);
34533
- if (!removing && draft.kind === "action" && published.executableHash) withBaselines(root, (baselines) => baselines.published(draft.id, draft.name, published.executableHash));
34537
+ if (!removing && draft.kind === "action" && published.executableHash) withBaselines(root, (baselines) => baselines.published(draft.id, draft.name, published.executableHash, /* @__PURE__ */ new Date(), published.resource.version));
34534
34538
  if (published.distribution && published.distribution.state !== "available") return ok4(`Published ${label(draft.kind)} ${draft.name}. Distribution is ${published.distribution.state}. ${published.distribution.reason === "confirmed-defect" ? "This executable revision was withdrawn. Continue developing and submit a corrected definition." : "You can continue developing and using your accepted revision. Wider distribution advances automatically as independent workspaces use it."}${published.distribution.until ? ` Protective pause ends ${published.distribution.until}.` : ""}
34535
34539
  Inspect distribution: ${command4(invocation, `apps history ${draft.appSlug} --resource ${backendKind(draft.kind)}:${draft.name}`)}
34536
34540
  `, true);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apilift",
3
- "version": "1.4.38",
3
+ "version": "1.4.39",
4
4
  "description": "Use logged-in apps and discover missing actions with Apilift.",
5
5
  "author": {
6
6
  "name": "Apilift"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apilift",
3
- "version": "1.4.38",
3
+ "version": "1.4.39",
4
4
  "description": "Use logged-in apps and discover missing actions with Apilift.",
5
5
  "author": {
6
6
  "name": "Apilift"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apilift/cli",
3
- "version": "1.4.38",
3
+ "version": "1.4.39",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,7 +36,7 @@
36
36
  "engines": {
37
37
  "node": ">=24.16.0 <27"
38
38
  },
39
- "gitHead": "0721266f35e01d43a91955e20c161dd7d98b97c8",
39
+ "gitHead": "f81711fbaca403338ca7ecb4695d27d684c09fe1",
40
40
  "repository": {
41
41
  "type": "git",
42
42
  "url": "git+https://github.com/egor-sergeev/apilift.git",