tines 0.0.331 → 0.0.333

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/index.js +100 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9768,6 +9768,63 @@ function object2(value) {
9768
9768
  function text3(value) {
9769
9769
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
9770
9770
  }
9771
+ var CODEX_USAGE_LIMIT = /^You['’]ve hit your usage limit/;
9772
+ var CODEX_QUOTA_EXCEEDED = /^Quota exceeded\./;
9773
+ var CODEX_PROVIDER_FAILURES = [
9774
+ /^Selected model is at capacity/,
9775
+ /^We['’]re currently experiencing high demand/
9776
+ ];
9777
+ var CODEX_MONTHS = [
9778
+ "jan",
9779
+ "feb",
9780
+ "mar",
9781
+ "apr",
9782
+ "may",
9783
+ "jun",
9784
+ "jul",
9785
+ "aug",
9786
+ "sep",
9787
+ "oct",
9788
+ "nov",
9789
+ "dec"
9790
+ ];
9791
+ var CODEX_RESET_MINUTE_MS = 6e4;
9792
+ function parseCodexResetTime(message3, now) {
9793
+ const stated = /\btry again at (.+?)\.?$/i.exec(message3.trim())?.[1];
9794
+ if (!stated) return null;
9795
+ const parts = /^(?:([A-Z][a-z]{2}) (\d{1,2})(?:st|nd|rd|th), (\d{4}) )?(\d{1,2}):(\d{2}) (AM|PM)$/.exec(
9796
+ stated
9797
+ );
9798
+ if (!parts) return null;
9799
+ const [, monthName, dayText, yearText, hourText, minuteText, meridiem] = parts;
9800
+ const hour = Number(hourText);
9801
+ const minute = Number(minuteText);
9802
+ if (hour < 1 || hour > 12 || minute > 59) return null;
9803
+ const hour24 = hour % 12 + (meridiem === "PM" ? 12 : 0);
9804
+ if (monthName === void 0) {
9805
+ const today = new Date(now);
9806
+ return new Date(today.getFullYear(), today.getMonth(), today.getDate(), hour24, minute).getTime() + CODEX_RESET_MINUTE_MS;
9807
+ }
9808
+ const month = CODEX_MONTHS.indexOf(monthName.toLowerCase());
9809
+ if (month === -1) return null;
9810
+ const day = Number(dayText);
9811
+ const date = new Date(Number(yearText), month, day, hour24, minute);
9812
+ if (date.getMonth() !== month || date.getDate() !== day) return null;
9813
+ return date.getTime() + CODEX_RESET_MINUTE_MS;
9814
+ }
9815
+ function classifyCodexFailure(message3, now) {
9816
+ const trimmed = message3.trim();
9817
+ const detail = clip(trimmed, 500);
9818
+ if (CODEX_USAGE_LIMIT.test(trimmed)) {
9819
+ const resumeAt = parseCodexResetTime(trimmed, now);
9820
+ return { kind: "rate_limited", detail, ...resumeAt !== null ? { resumeAt } : {} };
9821
+ }
9822
+ if (CODEX_QUOTA_EXCEEDED.test(trimmed)) return { kind: "rate_limited", detail };
9823
+ if (CODEX_PROVIDER_FAILURES.some((pattern) => pattern.test(trimmed))) {
9824
+ return { kind: "provider_error", detail };
9825
+ }
9826
+ return void 0;
9827
+ }
9771
9828
  function renderCodexEvent(event) {
9772
9829
  const type = event.type;
9773
9830
  if (type === "thread.started") return ["[session] started"];
@@ -9808,10 +9865,12 @@ function renderCodexEvent(event) {
9808
9865
  }
9809
9866
  }
9810
9867
  var CodexStreamRenderer = class {
9811
- constructor(emit) {
9868
+ constructor(emit, now = Date.now) {
9812
9869
  this.emit = emit;
9870
+ this.now = now;
9813
9871
  }
9814
9872
  emit;
9873
+ now;
9815
9874
  pending = "";
9816
9875
  collected = {
9817
9876
  pricingEvidence: {
@@ -9831,6 +9890,10 @@ var CodexStreamRenderer = class {
9831
9890
  threadId;
9832
9891
  stickyStatus;
9833
9892
  awaitingTerminal = false;
9893
+ /** How the current turn failed, when the provider refused or failed. */
9894
+ turnOutcome;
9895
+ /** A top-level `error` that nothing has followed yet. */
9896
+ trailingOutcome;
9834
9897
  write(chunk) {
9835
9898
  this.pending += chunk;
9836
9899
  let nl = this.pending.indexOf("\n");
@@ -9847,7 +9910,8 @@ var CodexStreamRenderer = class {
9847
9910
  this.pending = "";
9848
9911
  }
9849
9912
  summary() {
9850
- return copySummary(this.collected);
9913
+ const outcome = this.turnOutcome ?? this.trailingOutcome;
9914
+ return copySummary({ ...this.collected, ...outcome ? { harnessOutcome: outcome } : {} });
9851
9915
  }
9852
9916
  line(raw) {
9853
9917
  const trimmed = raw.trim();
@@ -9867,7 +9931,37 @@ var CodexStreamRenderer = class {
9867
9931
  for (const line of renderCodexEvent(event)) this.emit(`${line}
9868
9932
  `);
9869
9933
  }
9934
+ /**
9935
+ * Reads a refusal only from Codex's own `turn.failed` and `error` events,
9936
+ * never from an item's content: an agent message or command output that
9937
+ * quotes the usage-limit sentence must not hold the runner.
9938
+ */
9939
+ noteOutcome(event) {
9940
+ switch (event.type) {
9941
+ case "turn.started":
9942
+ case "turn.completed":
9943
+ this.turnOutcome = void 0;
9944
+ this.trailingOutcome = void 0;
9945
+ return;
9946
+ case "turn.failed":
9947
+ this.turnOutcome = classifyCodexFailure(
9948
+ text3(object2(event.error)?.message) ?? text3(event.message) ?? "",
9949
+ this.now()
9950
+ );
9951
+ this.trailingOutcome = void 0;
9952
+ return;
9953
+ case "error":
9954
+ this.trailingOutcome = classifyCodexFailure(text3(event.message) ?? "", this.now());
9955
+ return;
9956
+ case "item.started":
9957
+ case "item.updated":
9958
+ case "item.completed":
9959
+ this.trailingOutcome = void 0;
9960
+ return;
9961
+ }
9962
+ }
9870
9963
  collect(event) {
9964
+ this.noteOutcome(event);
9871
9965
  if (event.type === "thread.started" && validProviderSessionId(event.thread_id)) {
9872
9966
  if (this.threadId && this.threadId !== event.thread_id)
9873
9967
  this.stickyStatus = "multiple_threads";
@@ -11094,7 +11188,7 @@ function classifyExit(facts) {
11094
11188
  }
11095
11189
  if (facts.effortMismatch) return { status: "failed", error: mask(facts.effortMismatch) };
11096
11190
  if (facts.signal) return { status: "failed", error: `harness killed by ${facts.signal}` };
11097
- const outcome = facts.harness === "pi" ? facts.harnessOutcome : void 0;
11191
+ const outcome = facts.harnessOutcome;
11098
11192
  if (facts.code !== 0) {
11099
11193
  if (facts.limited) {
11100
11194
  const detail = mask(facts.limited.detail);
@@ -11120,7 +11214,7 @@ function classifyExit(facts) {
11120
11214
  error: `harness exited with code ${facts.code}`
11121
11215
  };
11122
11216
  }
11123
- if (outcome && outcome.kind !== "ok") {
11217
+ if (facts.harness === "pi" && outcome && outcome.kind !== "ok") {
11124
11218
  return providerVerdict(outcome, mask) ?? { status: "failed", error: `pi: ${mask(outcome.detail)}` };
11125
11219
  }
11126
11220
  return { status: "completed" };
@@ -11132,6 +11226,7 @@ function providerVerdict(outcome, mask) {
11132
11226
  status: "failed",
11133
11227
  error: `rate limited: ${detail}`,
11134
11228
  judgment: "rate_limited",
11229
+ ...outcome.resumeAt !== void 0 ? { resume_at: outcome.resumeAt } : {},
11135
11230
  note: `harness rate limited (${detail})`
11136
11231
  };
11137
11232
  }
@@ -12363,7 +12458,7 @@ async function runDaemon(opts) {
12363
12458
  timeoutMinutes: assignment.timeout_minutes,
12364
12459
  limited: run3.limiter?.signal() ?? null,
12365
12460
  providerError: run3.limiter?.providerError() ?? null,
12366
- // pi: its stream, not its exit code, says how the run ended.
12461
+ // The stream's own account of a provider refusal or failure.
12367
12462
  ...run3.renderer ? { harnessOutcome: run3.renderer.summary().harnessOutcome } : {},
12368
12463
  effortMismatch: run3.effortMismatch ?? null,
12369
12464
  secrets: secretEnvValues
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tines",
3
- "version": "0.0.331",
3
+ "version": "0.0.333",
4
4
  "description": "CLI for Tines, an orchestration layer for AI agents",
5
5
  "repository": {
6
6
  "type": "git",