halfcycle 0.9.0 → 0.11.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.
@@ -16748,6 +16748,15 @@ var BUILD_TASK_BUDGET_MAX_MINUTES = 10080;
16748
16748
  var BUILD_TASK_NOTE_MAX = 240;
16749
16749
  var BUILD_TASK_CLEAR_COMMAND_MAX = 240;
16750
16750
  var buildTaskKindSchema = external_exports.enum(["build", "review", "spec", "walk", "check"]);
16751
+ var BUILD_TASK_KIND_LABEL = Object.freeze({
16752
+ build: "Build",
16753
+ review: "Review",
16754
+ spec: "Spec",
16755
+ walk: "Walk",
16756
+ check: "Check"
16757
+ });
16758
+ var buildTaskShipsSchema = external_exports.enum(["deploy", "release"]);
16759
+ var BUILD_TASK_SHIPPED_MAX = 64;
16751
16760
  var buildTaskIdSchema = external_exports.string().min(1).max(64);
16752
16761
  var buildTaskSchema = external_exports.object({
16753
16762
  // The task's own id, as the coordinator's graph names it.
@@ -16800,7 +16809,19 @@ var buildTaskSchema = external_exports.object({
16800
16809
  clearCommand: descriptionText(BUILD_TASK_CLEAR_COMMAND_MAX).optional(),
16801
16810
  // On a task held for a person, the crew member ready to take it on once it is
16802
16811
  // cleared, by callsign. Absent when nobody is standing by.
16803
- standingBy: agentNameSchema.optional()
16812
+ standingBy: agentNameSchema.optional(),
16813
+ // The batch this task was started in, counted from 0. A record of when it ran,
16814
+ // sent only once it has run; absent on a task not started yet.
16815
+ wave: external_exports.number().int().min(0).max(BUILD_SNAPSHOT_TASK_LIMIT).optional(),
16816
+ // True when the task is a check of the whole project rather than of one piece of
16817
+ // work. Absent otherwise; never false.
16818
+ wholeProject: external_exports.literal(true).optional(),
16819
+ // How the task ships what was built: deployed, or released. Absent on a task that
16820
+ // ships nothing.
16821
+ ships: buildTaskShipsSchema.optional(),
16822
+ // The commit or version the task reported when it shipped, exactly as reported, as
16823
+ // one word with no spaces. Absent until it has shipped, or when it reported none.
16824
+ shipped: external_exports.string().min(1).max(BUILD_TASK_SHIPPED_MAX).regex(/^[^\s\x00-\x1f\x7f-\x9f]*$/, { message: "must be one word, with no spaces or control characters" }).optional()
16804
16825
  }).strict().superRefine((task, ctx) => {
16805
16826
  if (task.reviews !== void 0 && task.reviews === task.id) {
16806
16827
  ctx.addIssue({
@@ -17216,7 +17237,26 @@ var timeRunNotSentSchema = external_exports.object({
17216
17237
  reason: external_exports.enum(TIME_NOT_SENT_REASONS)
17217
17238
  }).strict();
17218
17239
  var timeRunResultSchema = external_exports.discriminatedUnion("outcome", [timeRunRecordedSchema, timeRunNotSentSchema]);
17219
- var TIME_TOTAL_KEYS = ["building", "reviewing", "lateFixes", "waitingOnPerson", "waitingOnMachine"];
17240
+ var TIME_ROW_KEYS = [
17241
+ "building",
17242
+ "reviewing",
17243
+ "lateFixes",
17244
+ "checks",
17245
+ "walks",
17246
+ "untied",
17247
+ "waitingOnPerson",
17248
+ "waitingOnMachine"
17249
+ ];
17250
+ var timeRowsSchema = external_exports.object({
17251
+ building: timeCountSchema,
17252
+ reviewing: timeCountSchema,
17253
+ lateFixes: timeCountSchema,
17254
+ checks: timeCountSchema,
17255
+ walks: timeCountSchema,
17256
+ untied: timeCountSchema,
17257
+ waitingOnPerson: timeCountSchema,
17258
+ waitingOnMachine: timeCountSchema
17259
+ }).strict();
17220
17260
  var timeTotalsSchema = external_exports.object({
17221
17261
  building: timeCountSchema,
17222
17262
  reviewing: timeCountSchema,
@@ -17239,25 +17279,70 @@ var timeUsageTotalsSchema = external_exports.object({
17239
17279
  ctx.addIssue({ code: "custom", path: [key], message: "must be 0 when no usage could be read" });
17240
17280
  }
17241
17281
  });
17282
+ var timeReviewCountsSchema = external_exports.object({
17283
+ attempts: timeCountSchema,
17284
+ reviews: timeCountSchema,
17285
+ failed: timeCountSchema
17286
+ }).strict().superRefine((v, ctx) => {
17287
+ if (v.failed > v.reviews)
17288
+ ctx.addIssue({ code: "custom", path: ["failed"], message: "must not be more than the reviews" });
17289
+ });
17290
+ var timeKindSchema = external_exports.object({
17291
+ kind: buildTaskKindSchema.nullable(),
17292
+ workingSeconds: timeCountSchema,
17293
+ budgetMinutes: timeCountSchema.nullable()
17294
+ }).strict();
17242
17295
  var timeTaskSummarySchema = external_exports.object({
17243
17296
  task: buildTaskIdSchema,
17244
17297
  title: buildTaskSchema.shape.title.optional(),
17298
+ clock: timeRowsSchema,
17299
+ agent: timeRowsSchema,
17300
+ permissionPromptSeconds: timeCountSchema,
17245
17301
  totals: timeTotalsSchema,
17246
17302
  usage: timeUsageTotalsSchema
17247
17303
  }).strict();
17304
+ var TIME_KINDS_MAX = buildTaskKindSchema.options.length + 1;
17305
+ var timeSharePercentSchema = external_exports.number().nonnegative().finite();
17306
+ var timeShareKindSchema = external_exports.object({
17307
+ kind: external_exports.enum(TIME_ROW_KEYS),
17308
+ percent: timeSharePercentSchema
17309
+ }).strict();
17310
+ var timeShareKindsSchema = external_exports.array(timeShareKindSchema).max(TIME_ROW_KEYS.length).superRefine((kinds, ctx) => {
17311
+ const seen = /* @__PURE__ */ new Set();
17312
+ kinds.forEach((entry, i) => {
17313
+ if (seen.has(entry.kind))
17314
+ ctx.addIssue({ code: "custom", path: [i, "kind"], message: "lists one kind twice" });
17315
+ seen.add(entry.kind);
17316
+ });
17317
+ });
17318
+ var timeSummaryShareSchema = external_exports.discriminatedUnion("state", [
17319
+ external_exports.object({ state: external_exports.literal("unknown") }).strict(),
17320
+ external_exports.object({
17321
+ state: external_exports.literal("known"),
17322
+ weekCeilingTokens: external_exports.number().int().positive(),
17323
+ spentPercent: timeSharePercentSchema,
17324
+ kinds: timeShareKindsSchema
17325
+ }).strict()
17326
+ ]);
17248
17327
  var timeSummarySchema = external_exports.object({
17249
17328
  phase: phaseNameSchema,
17250
17329
  closed: external_exports.boolean(),
17251
17330
  openedAt: utcIso8601,
17252
17331
  closedAt: utcIso8601.nullable(),
17253
17332
  elapsedSeconds: timeCountSchema,
17333
+ clock: timeRowsSchema,
17334
+ agent: timeRowsSchema,
17335
+ permissionPromptSeconds: timeCountSchema,
17254
17336
  totals: timeTotalsSchema,
17255
17337
  usage: timeUsageTotalsSchema,
17338
+ reviews: timeReviewCountsSchema,
17339
+ kinds: external_exports.array(timeKindSchema).max(TIME_KINDS_MAX, { message: `can list at most ${TIME_KINDS_MAX} kinds` }),
17256
17340
  tasks: external_exports.array(timeTaskSummarySchema).max(BUILD_SNAPSHOT_TASK_LIMIT, { message: `can list at most ${BUILD_SNAPSHOT_TASK_LIMIT} tasks` }),
17257
17341
  notTiedToATask: external_exports.object({
17258
17342
  totals: timeTotalsSchema,
17259
17343
  usage: timeUsageTotalsSchema
17260
- }).strict()
17344
+ }).strict(),
17345
+ share: timeSummaryShareSchema.optional()
17261
17346
  }).strict().superRefine((v, ctx) => {
17262
17347
  if (v.closed !== (v.closedAt !== null)) {
17263
17348
  ctx.addIssue({ code: "custom", path: ["closedAt"], message: "must be set exactly when the phase is closed" });
@@ -17268,18 +17353,30 @@ var timeSummarySchema = external_exports.object({
17268
17353
  ctx.addIssue({ code: "custom", path: ["tasks", i, "task"], message: "lists one task twice" });
17269
17354
  seen.add(row.task);
17270
17355
  });
17356
+ const kinds = /* @__PURE__ */ new Set();
17357
+ v.kinds.forEach((entry, i) => {
17358
+ if (kinds.has(entry.kind))
17359
+ ctx.addIssue({ code: "custom", path: ["kinds", i, "kind"], message: "lists one kind twice" });
17360
+ kinds.add(entry.kind);
17361
+ });
17271
17362
  });
17272
17363
  var timeTraceIntervalSchema = external_exports.object({
17273
17364
  ...timeIntervalSchema.shape,
17274
- counted: external_exports.enum(TIME_TOTAL_KEYS),
17365
+ counted: external_exports.enum(TIME_ROW_KEYS).nullable(),
17275
17366
  countedSeconds: timeCountSchema
17276
17367
  }).strict().superRefine((v, ctx) => {
17277
- const { counted: _counted, countedSeconds: _countedSeconds, ...stored } = v;
17368
+ const { counted, countedSeconds, ...stored } = v;
17278
17369
  const result = timeIntervalSchema.safeParse(stored);
17279
- if (result.success)
17280
- return;
17281
- for (const issue2 of result.error.issues)
17282
- ctx.addIssue({ code: "custom", path: issue2.path, message: issue2.message });
17370
+ if (!result.success) {
17371
+ for (const issue2 of result.error.issues)
17372
+ ctx.addIssue({ code: "custom", path: issue2.path, message: issue2.message });
17373
+ }
17374
+ if (counted === null !== (v.state === "waiting-person")) {
17375
+ ctx.addIssue({ code: "custom", path: ["counted"], message: "is empty exactly for a permission prompt" });
17376
+ }
17377
+ if (counted === null && countedSeconds !== 0) {
17378
+ ctx.addIssue({ code: "custom", path: ["countedSeconds"], message: "must be 0 for a permission prompt" });
17379
+ }
17283
17380
  });
17284
17381
  var timeTraceApprovalWaitSchema = external_exports.object({
17285
17382
  planVersion: planVersionSchema,
@@ -17287,10 +17384,17 @@ var timeTraceApprovalWaitSchema = external_exports.object({
17287
17384
  to: utcIso8601.nullable(),
17288
17385
  countedSeconds: timeCountSchema
17289
17386
  }).strict();
17387
+ var timeTraceDeclaredWaitSchema = external_exports.object({
17388
+ declaration: external_exports.uuid(),
17389
+ from: utcIso8601,
17390
+ to: utcIso8601.nullable(),
17391
+ countedSeconds: timeCountSchema
17392
+ }).strict();
17290
17393
  var timeTraceSchema = external_exports.object({
17291
17394
  summary: timeSummarySchema,
17292
17395
  intervals: external_exports.array(timeTraceIntervalSchema),
17293
17396
  approvalWaits: external_exports.array(timeTraceApprovalWaitSchema),
17397
+ declaredWaits: external_exports.array(timeTraceDeclaredWaitSchema),
17294
17398
  usage: external_exports.array(timeUsageSchema)
17295
17399
  }).strict();
17296
17400
  function timeDurationLabelOf(seconds) {
@@ -17303,6 +17407,16 @@ function timeDurationLabelOf(seconds) {
17303
17407
  return `${String(hours)} h ${String(minutes).padStart(2, "0")} min`;
17304
17408
  }
17305
17409
 
17410
+ // ../events/dist/repository-facts.js
17411
+ var REPOSITORY_FACTS = Object.freeze(["database", "browser-app", "inbound-webhooks"]);
17412
+ var repositoryFactSchema = external_exports.enum(REPOSITORY_FACTS);
17413
+ var repositoryFactsSchema = external_exports.array(repositoryFactSchema).max(REPOSITORY_FACTS.length).refine((facts) => new Set(facts).size === facts.length, { message: "must name each fact once" });
17414
+ var REPOSITORY_FACT_WORDS = Object.freeze({
17415
+ database: "database",
17416
+ "browser-app": "web front end",
17417
+ "inbound-webhooks": "incoming webhooks"
17418
+ });
17419
+
17306
17420
  // ../events/dist/code-link.js
17307
17421
  var codeLinkAddressSchema = external_exports.string().regex(GITHUB_REPOSITORY_ADDRESS, { message: "must be a GitHub repository address: https://github.com/<owner>/<repo>" }).refine((v) => !looksLikeSecret(v), { message: "looks like a password or key, so it is not sent" });
17308
17422
  var codeLinkFactsSchema = external_exports.object({
@@ -18032,7 +18146,7 @@ var phaseWorkSchema = external_exports.object({
18032
18146
  var STANDING_RULE_FILE = "docs/invariants.md";
18033
18147
  var CHANGE_MOVED_BY = Object.freeze(["sort", "person", "self"]);
18034
18148
  var changeMovedBySchema = external_exports.enum(CHANGE_MOVED_BY);
18035
- var PHASE_PATH_SET_BY = Object.freeze(["open", "person", "self"]);
18149
+ var PHASE_PATH_SET_BY = Object.freeze(["open", "person", "self", "size"]);
18036
18150
  var phasePathSetBySchema = external_exports.enum(PHASE_PATH_SET_BY);
18037
18151
  var SMALL_CHANGE_OUTCOMES = Object.freeze(["done", "moved-up", "stopped"]);
18038
18152
  var smallChangeOutcomeSchema = external_exports.enum(SMALL_CHANGE_OUTCOMES);
@@ -18603,789 +18717,963 @@ function shownReviewEntryOf(view, shownAt) {
18603
18717
  };
18604
18718
  }
18605
18719
 
18606
- // ../events/dist/client-version.js
18607
- var CLIENT_VERSION_HEADER = "x-halfcycle-client";
18608
- var TYPED_APPROVAL_OFF_SENTENCE = "Approving by typing is switched off for now.";
18609
- var HEADLESS_CANNOT_APPROVE = "(a headless session cannot approve, by design)";
18610
- var TYPED_REVIEW_OFF_SENTENCE = "Reviewing by typing is switched off for now.";
18611
- var CLIENT_UPDATE_SENTENCE = `To approve by typing in a session, Halfcycle needs updating: run \`${HALFCYCLE_COMMAND}\` in this project's folder, then reopen your session and ask again.`;
18612
-
18613
- // ../events/dist/device-auth.js
18614
- var utcIso86012 = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
18615
- var DEVICE_AUTH_STATUS = {
18616
- /** The poll: minted, nobody has acted yet. Keep polling. 200. */
18617
- PENDING: "pending",
18618
- /** The poll: approved, and the credential is in this body. Once only. 200. */
18619
- APPROVED: "approved",
18620
- /** Confirm: the decision was recorded (approve or decline). 200. */
18621
- RECORDED: "recorded",
18622
- /** The human refused at the confirmation page. 400 on the poll. */
18623
- DECLINED: "declined",
18624
- /** The code's absolute expiry has passed. 400. */
18625
- EXPIRED: "expired",
18626
- /** Never minted, or already spent. 400. */
18627
- UNKNOWN: "unknown",
18628
- /** Confirm: neither `approve` nor `decline` was named. Nothing was bound. 400. */
18629
- INVALID_DECISION: "invalid-decision",
18630
- /** Confirm: an approval arrived with no signed-in identity. 400. */
18631
- IDENTITY_REQUIRED: "identity-required",
18632
- /** This deployment has no `SIGNIN_BASE_URL` / `DEVICE_AUTH_CONFIRM_SECRET`. 503. */
18633
- NOT_CONFIGURED: "not-configured",
18634
- /** Confirm: the shared confirmation secret was missing or wrong. 401. */
18635
- UNAUTHORIZED: "unauthorized",
18636
- /** Confirm: the code was already approved, declined or claimed. 409. */
18637
- ALREADY_DECIDED: "already-decided",
18638
- /**
18639
- * Confirm: an approval named a signed-in identity whose account has not accepted
18640
- * the terms of service and privacy policy currently in force. 400.
18641
- *
18642
- * NOT BOUND TO THE DEVICE CODE. Unlike every other refusal in this const, this one
18643
- * says nothing about the code itself — the code is still `pending` after this
18644
- * response, exactly as it was before the request, so the SAME userCode can be
18645
- * confirmed again once the caller has recorded acceptance. `deviceConfirmRequestSchema`'s
18646
- * `acceptTerms` is how a second confirm says it did.
18647
- */
18648
- TERMS_REQUIRED: "terms-required"
18649
- };
18650
- var DEVICE_POLL_REFUSALS = [
18651
- DEVICE_AUTH_STATUS.DECLINED,
18652
- DEVICE_AUTH_STATUS.EXPIRED,
18653
- DEVICE_AUTH_STATUS.UNKNOWN
18654
- ];
18655
- var deviceAuthStartResponseSchema = external_exports.object({
18656
- deviceCode: external_exports.string().min(1),
18657
- userCode: external_exports.string().min(1),
18658
- verificationUrl: external_exports.string().min(1),
18659
- expiresAt: utcIso86012,
18660
- pollIntervalMs: external_exports.number().int().positive()
18661
- }).strict();
18662
- var devicePollPendingSchema = external_exports.object({
18663
- status: external_exports.literal(DEVICE_AUTH_STATUS.PENDING),
18664
- pollIntervalMs: external_exports.number().int().positive()
18665
- }).strict();
18666
- var devicePollApprovedSchema = external_exports.object({
18667
- status: external_exports.literal(DEVICE_AUTH_STATUS.APPROVED),
18668
- accountId: external_exports.string().min(1),
18669
- credential: external_exports.string().min(1),
18670
- expiresAt: utcIso86012
18671
- }).strict();
18672
- var deviceAuthRefusalSchema = external_exports.object({
18673
- status: external_exports.enum([
18674
- DEVICE_AUTH_STATUS.DECLINED,
18675
- DEVICE_AUTH_STATUS.EXPIRED,
18676
- DEVICE_AUTH_STATUS.UNKNOWN,
18677
- DEVICE_AUTH_STATUS.INVALID_DECISION,
18678
- DEVICE_AUTH_STATUS.IDENTITY_REQUIRED,
18679
- DEVICE_AUTH_STATUS.NOT_CONFIGURED,
18680
- DEVICE_AUTH_STATUS.UNAUTHORIZED,
18681
- DEVICE_AUTH_STATUS.ALREADY_DECIDED,
18682
- DEVICE_AUTH_STATUS.TERMS_REQUIRED
18683
- ]),
18684
- message: external_exports.string().min(1)
18685
- }).strict();
18686
- var devicePollResponseSchema = external_exports.union([
18687
- devicePollPendingSchema,
18688
- devicePollApprovedSchema,
18689
- deviceAuthRefusalSchema
18690
- ]);
18691
- var deviceConfirmRequestSchema = external_exports.object({
18692
- userCode: external_exports.string().min(1),
18693
- decision: external_exports.enum(["approve", "decline"]),
18694
- externalAuthId: external_exports.string().min(1).optional(),
18695
- email: external_exports.string().optional(),
18696
- acceptTerms: external_exports.boolean().optional()
18697
- }).strict();
18698
- var deviceConfirmRecordedSchema = external_exports.object({
18699
- status: external_exports.literal(DEVICE_AUTH_STATUS.RECORDED),
18700
- accountId: external_exports.string().min(1).optional()
18701
- }).strict();
18702
- var deviceConfirmResponseSchema = external_exports.union([
18703
- deviceConfirmRecordedSchema,
18704
- deviceAuthRefusalSchema
18705
- ]);
18706
- var DASHBOARD_URL = "https://app.halfcycle.ai";
18707
-
18708
- // ../events/dist/project-page.js
18709
- function projectPageUrl(engagementId) {
18710
- return isEngagementId(engagementId) ? `${DASHBOARD_URL}/${encodeURIComponent(engagementId)}` : void 0;
18720
+ // ../events/dist/stop-words.js
18721
+ var NOTHING_NEEDED = "Nothing is needed from you.";
18722
+ function isOneLine(v) {
18723
+ return !/[\r\n]/.test(v);
18711
18724
  }
18712
- function systemViewUrl(engagementId, phase) {
18713
- const page = projectPageUrl(engagementId);
18714
- if (page === void 0)
18715
- return void 0;
18716
- return `${page}/console/system-view?${new URLSearchParams({ phase }).toString()}`;
18725
+ function asksSomething(v) {
18726
+ return v.includes("?");
18717
18727
  }
18718
- function requestAccessSentence(engagementId) {
18719
- const page = projectPageUrl(engagementId) ?? "this project's page on Halfcycle";
18720
- return `To ask for access, open ${page} signed in as this account and choose "Request access". Then let the project's owner know, so they can approve it.`;
18728
+ function saysNothingIsNeeded(yourPart) {
18729
+ return yourPart === null || typeof yourPart === "string" && yourPart.trim() === NOTHING_NEEDED;
18721
18730
  }
18722
-
18723
- // ../events/dist/scope-notices.js
18724
- var scopePartNameSchema = external_exports.string().min(1).max(SYSTEM_PART_NAME_MAX);
18725
- var scopeNoteEntrySchema = external_exports.object({
18726
- part: scopePartNameSchema.optional(),
18727
- note: external_exports.string().min(1).max(SCOPE_NOTE_MAX)
18728
- }).strict();
18729
- var scopeSentBackSchema = external_exports.object({
18730
- phase: scopePhaseSchema,
18731
- number: external_exports.number().int().min(1),
18732
- notes: external_exports.array(scopeNoteEntrySchema).min(1).max(PHASE_PLAN_CHANGES_MAX + 1)
18733
- }).strict().superRefine((entry, ctx) => {
18734
- entry.notes.forEach((n, i) => {
18735
- if (n.part === void 0 && i !== entry.notes.length - 1) {
18736
- ctx.addIssue({
18737
- code: external_exports.ZodIssueCode.custom,
18738
- path: ["notes", i],
18739
- message: "the note on the whole phase comes after every note on a part"
18740
- });
18741
- }
18742
- });
18743
- });
18744
- var scopeWaitingSchema = external_exports.object({
18745
- phase: scopePhaseSchema,
18746
- number: external_exports.number().int().min(1),
18747
- changedParts: external_exports.array(scopePartNameSchema).max(PHASE_PLAN_CHANGES_MAX)
18748
- }).strict();
18749
- var scopeNoticesSchema = external_exports.object({
18750
- sentBack: external_exports.array(scopeSentBackSchema),
18751
- waiting: external_exports.array(scopeWaitingSchema),
18752
- typedApprovalOffered: external_exports.boolean()
18753
- }).strict().refine((v) => v.sentBack.length > 0 || v.waiting.length > 0, {
18754
- message: "carried only when a phase was sent back or waits for the owner"
18731
+ var stopPartSchema = external_exports.string().refine((v) => v.trim().length > 0, { message: "must say something" }).refine(isOneLine, { message: "must be one line" });
18732
+ var stopWordsSchema = external_exports.object({
18733
+ happened: stopPartSchema,
18734
+ yourPart: stopPartSchema.nullable(),
18735
+ next: stopPartSchema
18736
+ }).strict().refine((w) => !(saysNothingIsNeeded(w.yourPart) && (asksSomething(w.happened) || asksSomething(w.next))), {
18737
+ message: "asks the person something, so it cannot say nothing is needed from them",
18738
+ path: ["yourPart"]
18755
18739
  });
18756
- var scopeApprovedNoticeSchema = external_exports.object({ phase: scopePhaseSchema }).strict();
18757
- var scopeApprovedNoticesSchema = external_exports.array(scopeApprovedNoticeSchema);
18758
- function approvedInConsoleSentence(phase) {
18759
- return `Scope for ${phaseWords(phase)} was approved in the console; continuing. Say so to the person, then carry on with the next step; they do not need to tell you.`;
18740
+ function stopTextOf(words) {
18741
+ return `${words.happened} ${words.yourPart ?? NOTHING_NEEDED} ${words.next}`;
18760
18742
  }
18761
- function quotedParts(parts) {
18762
- return parts.map((p) => `"${p}"`).join(", ");
18743
+
18744
+ // ../events/dist/pacing.js
18745
+ var CAREFUL_START_AGENTS = 3;
18746
+ var KEEP_FREE_MAX = 90;
18747
+ var keepFreePercentSchema = external_exports.number().int().min(0).max(KEEP_FREE_MAX);
18748
+ var KEEP_FREE_DEFAULT = 20;
18749
+ var PACE_AGENTS_MAX = 20;
18750
+ var FIVE_HOUR_WINDOW_MINUTES = 300;
18751
+ var SEVEN_DAY_WINDOW_MINUTES = 10080;
18752
+ var PACE_WINDOW_MINUTES_MAX = 525600;
18753
+ var MINUTE_KEY = /^([1-9][0-9]{0,6})-minute$/;
18754
+ function isPaceWindowKind(value) {
18755
+ if (value === "five-hour" || value === "seven-day")
18756
+ return true;
18757
+ if (typeof value !== "string")
18758
+ return false;
18759
+ const match = MINUTE_KEY.exec(value);
18760
+ if (match === null)
18761
+ return false;
18762
+ const minutes = Number(match[1]);
18763
+ return minutes <= PACE_WINDOW_MINUTES_MAX && minutes !== FIVE_HOUR_WINDOW_MINUTES && minutes !== SEVEN_DAY_WINDOW_MINUTES;
18763
18764
  }
18764
- function approvalSentence(phase, offerTyping) {
18765
- if (offerTyping === "off")
18766
- return TYPED_APPROVAL_OFF_SENTENCE;
18767
- if (offerTyping === "update")
18768
- return CLIENT_UPDATE_SENTENCE;
18769
- return `In their own interactive session ${HEADLESS_CANNOT_APPROVE}, they ask to see the plan, check each part, and then type: approve ${phase}.`;
18765
+ var paceWindowKindSchema = external_exports.custom((value) => isPaceWindowKind(value), {
18766
+ message: "not a usage window key"
18767
+ });
18768
+ function paceWindowMinutesOf(kind) {
18769
+ if (kind === "five-hour")
18770
+ return FIVE_HOUR_WINDOW_MINUTES;
18771
+ if (kind === "seven-day")
18772
+ return SEVEN_DAY_WINDOW_MINUTES;
18773
+ return Number(MINUTE_KEY.exec(kind)?.[1] ?? 0);
18770
18774
  }
18771
- function phaseWords(phase) {
18772
- return `phase "${phase}"`;
18775
+ function paceWindowKindOf(minutes) {
18776
+ if (!Number.isInteger(minutes) || minutes < 1 || minutes > PACE_WINDOW_MINUTES_MAX)
18777
+ return null;
18778
+ if (minutes === FIVE_HOUR_WINDOW_MINUTES)
18779
+ return "five-hour";
18780
+ if (minutes === SEVEN_DAY_WINDOW_MINUTES)
18781
+ return "seven-day";
18782
+ return `${String(minutes)}-minute`;
18773
18783
  }
18774
- function sentBackNotice(entry) {
18775
- return [
18776
- `Before anything else: the project's owner sent the scope of ${phaseWords(entry.phase)} back with these notes.`,
18777
- ...entry.notes.map((n) => n.part === void 0 ? `- ${n.note}` : `- "${n.part}": ${n.note}`),
18778
- `Change the plan to answer every note, then send it again: say "plan the next phase" to start. It then waits for the owner's approval again.`
18779
- ].join("\n");
18784
+ function paceWindowWordsOf(kind) {
18785
+ const minutes = paceWindowMinutesOf(kind);
18786
+ if (minutes === FIVE_HOUR_WINDOW_MINUTES)
18787
+ return "5-hour window";
18788
+ if (minutes === SEVEN_DAY_WINDOW_MINUTES)
18789
+ return "weekly window";
18790
+ if (minutes % 1440 === 0)
18791
+ return `${String(minutes / 1440)}-day window`;
18792
+ if (minutes % 60 === 0)
18793
+ return `${String(minutes / 60)}-hour window`;
18794
+ return `${String(minutes)}-minute window`;
18780
18795
  }
18781
- function systemViewSentence(offerTyping, address) {
18782
- if (address === void 0)
18783
- return [];
18784
- return [offerTyping === "yes" ? `Or they approve it in the System view: ${address}` : `They approve it in the System view: ${address}`];
18796
+ function paceShareWordsOf(keepFreePercent) {
18797
+ return keepFreePercent === 20 ? "a fifth" : `${String(keepFreePercent)}%`;
18785
18798
  }
18786
- function waitingNotice(entry, offerTyping, engagementId) {
18787
- return [
18788
- ...entry.changedParts.length > 0 ? [`These parts changed since the owner last decided on this phase, and wait for their approval: ${quotedParts(entry.changedParts)}.`] : [],
18789
- `The scope of ${phaseWords(entry.phase)} is waiting for the project's owner to approve it.`,
18790
- approvalSentence(entry.phase, offerTyping),
18791
- ...systemViewSentence(offerTyping, systemViewUrl(engagementId, entry.phase))
18792
- ].join(" ");
18793
- }
18794
- function scopeNoticeText(scope, options) {
18795
- const omit2 = options.omitActivePhase === true ? options.activePhase : void 0;
18796
- return {
18797
- sentBack: scope.sentBack.map(sentBackNotice),
18798
- waiting: scope.waiting.filter((w) => omit2 === void 0 || w.phase !== omit2).map((w) => waitingNotice(w, options.offerTyping, options.engagementId))
18799
- };
18800
- }
18801
-
18802
- // ../events/dist/review-notices.js
18803
- var reviewNoticeQuestionSchema = external_exports.object({
18804
- phase: scopePhaseSchema,
18805
- number: reviewPhaseNumberSchema,
18806
- part: systemPartIdSchema,
18807
- partName: systemPartSchema.shape.name,
18808
- question: reviewNoteSchema
18809
- }).strict();
18810
- var reviewNoticeSentBackSchema = external_exports.object({
18811
- phase: scopePhaseSchema,
18812
- number: reviewPhaseNumberSchema,
18813
- part: systemPartIdSchema,
18814
- partName: systemPartSchema.shape.name,
18815
- note: reviewNoteSchema
18799
+ var PACE_STATES = ["full", "slowing", "holding"];
18800
+ var paceStateSchema = external_exports.enum(PACE_STATES);
18801
+ var PACE_SHARE_GIVEN_IN = ["chat", "agent", "relayed"];
18802
+ var paceShareGivenInSchema = external_exports.enum(PACE_SHARE_GIVEN_IN);
18803
+ var paceShareRequestSchema = external_exports.object({ keepFreePercent: keepFreePercentSchema, givenIn: paceShareGivenInSchema }).strict();
18804
+ var paceShareResultSchema = external_exports.discriminatedUnion("outcome", [
18805
+ external_exports.object({
18806
+ outcome: external_exports.literal("recorded"),
18807
+ keepFreePercent: keepFreePercentSchema,
18808
+ moreAllowance: external_exports.boolean().optional()
18809
+ }).strict(),
18810
+ external_exports.object({ outcome: external_exports.literal("needs-the-person"), keepFreePercent: keepFreePercentSchema }).strict(),
18811
+ external_exports.object({ outcome: external_exports.literal("not-owner") }).strict(),
18812
+ external_exports.object({ outcome: external_exports.literal("no-owner") }).strict(),
18813
+ external_exports.object({ outcome: external_exports.literal("unavailable") }).strict()
18814
+ ]);
18815
+ var pacingBasisTaskSchema = external_exports.object({
18816
+ id: buildTaskIdSchema,
18817
+ dependsOn: external_exports.array(buildTaskIdSchema).max(BUILD_SNAPSHOT_TASK_LIMIT),
18818
+ budgetMinutes: external_exports.number().int().min(1).max(BUILD_TASK_BUDGET_MAX_MINUTES).optional(),
18819
+ human: external_exports.boolean(),
18820
+ kind: buildTaskKindSchema.optional()
18816
18821
  }).strict();
18817
- var reviewNoticesSchema = external_exports.object({
18818
- questions: external_exports.array(reviewNoticeQuestionSchema).max(REVIEW_PARTS_MAX),
18819
- sentBack: external_exports.array(reviewNoticeSentBackSchema).max(REVIEW_PARTS_MAX)
18820
- }).strict().refine((v) => v.questions.length > 0 || v.sentBack.length > 0, {
18821
- message: "carried only when a question waits or a part was sent back"
18822
- });
18823
- function phaseCalled(phase) {
18824
- return `phase "${phase}"`;
18825
- }
18826
- function questionNotice(entry) {
18827
- const answer = `${HALFCYCLE_COMMAND} review ${quoteForShell(entry.phase)} --answer ${quoteForShell(entry.part)} "<your answer>"`;
18828
- return `Before anything else: the project's owner asked about "${entry.partName}" in the review of ${phaseCalled(entry.phase)}: ${entry.question} Answer it with ${answer}.`;
18829
- }
18830
- function sentBackNotice2(entry) {
18831
- return `Before anything else: the project's owner sent "${entry.partName}" in ${phaseCalled(entry.phase)} back to building: ${entry.note} Rebuild it: add or reopen the tasks that build it. For each task that rebuilds it, add a task that reviews the rebuild, held by a different agent from the one that built it. When each review has passed, report the build with the time each task finished, then run the close step again.`;
18822
+ var PACE_DEFAULT_TASK_MINUTES = 30;
18823
+ var PACE_DURATION_ROUND_MINUTES = 10;
18824
+ function taskMinutesOf(task, medianTaskMinutes) {
18825
+ if (task.human)
18826
+ return 0;
18827
+ if (task.budgetMinutes !== void 0)
18828
+ return task.budgetMinutes;
18829
+ if (medianTaskMinutes !== null && medianTaskMinutes > 0)
18830
+ return medianTaskMinutes;
18831
+ return PACE_DEFAULT_TASK_MINUTES;
18832
18832
  }
18833
- function reviewNoticeText(review) {
18834
- return [...review.questions.map(questionNotice), ...review.sentBack.map(sentBackNotice2)];
18833
+ function paceDurationRoundedOf(minutes) {
18834
+ return Math.ceil(minutes / PACE_DURATION_ROUND_MINUTES) * PACE_DURATION_ROUND_MINUTES;
18835
18835
  }
18836
-
18837
- // ../events/dist/credential-wire.js
18838
- var engagementCredentialResponseSchema = external_exports.object({
18839
- engagementId: external_exports.string(),
18840
- sessionToken: external_exports.string(),
18841
- mcpUrl: external_exports.string(),
18842
- guardUrl: external_exports.string(),
18843
- controlTelemetryUrl: external_exports.string(),
18844
- methodVersion: external_exports.string().optional(),
18845
- consoleUrl: external_exports.string().optional()
18836
+ var pacingBasisSchema = external_exports.object({
18837
+ outcome: external_exports.literal("basis"),
18838
+ claudePlan: external_exports.string().nullable().optional(),
18839
+ keepFreePercent: keepFreePercentSchema,
18840
+ settingsUrl: external_exports.string().optional(),
18841
+ phase: phaseNameSchema.nullable(),
18842
+ tasks: external_exports.array(pacingBasisTaskSchema).max(BUILD_SNAPSHOT_TASK_LIMIT),
18843
+ medianTaskMinutes: external_exports.number().nonnegative().nullable(),
18844
+ medianTaskTokens: external_exports.number().nonnegative().nullable()
18846
18845
  }).strict();
18847
- var ciTokenExchangeResponseSchema = external_exports.object({
18848
- token: external_exports.string(),
18849
- expiresAt: utcIso8601,
18850
- engagementId: external_exports.string()
18846
+ var paceSuggestionSchema = external_exports.object({
18847
+ number: external_exports.number().int().min(1).max(PACE_AGENTS_MAX),
18848
+ minutes: external_exports.number().int().nonnegative().nullable(),
18849
+ pauses: external_exports.number().int().nonnegative(),
18850
+ keepFreePercent: keepFreePercentSchema,
18851
+ knownWindow: paceWindowKindSchema.nullable(),
18852
+ expectedTokens: external_exports.number().int().nonnegative().optional(),
18853
+ limitTokens: external_exports.number().int().positive().optional(),
18854
+ multiple: external_exports.number().positive().nullable().optional(),
18855
+ firstGuess: external_exports.boolean().optional(),
18856
+ limitSetBy: external_exports.enum(["default", "person", "other"]).optional(),
18857
+ weekCeilingTokens: external_exports.number().int().positive().optional(),
18858
+ workMinutes: external_exports.number().int().nonnegative().nullable().optional()
18851
18859
  }).strict();
18852
- var NO_CREDENTIAL_PLACEHOLDER = "halfcycle:no-credential";
18853
- var NOT_THIS_ACCOUNT_PLACEHOLDER = "halfcycle:not-this-account";
18854
- var UPDATE_CLAUDE_CODE_PLACEHOLDER = "halfcycle:update-claude-code";
18855
- var CREDENTIAL_PLACEHOLDERS = [
18856
- NO_CREDENTIAL_PLACEHOLDER,
18857
- NOT_THIS_ACCOUNT_PLACEHOLDER,
18858
- UPDATE_CLAUDE_CODE_PLACEHOLDER
18859
- ];
18860
- var SWITCH_ACCOUNT_ANSWER = 'answer "s" when asked';
18861
- var NO_CREDENTIAL_SENTENCE = 'Halfcycle is not connected: this machine has no working Halfcycle credential for this project. Run "npx halfcycle" in the project folder to sign in again.';
18862
- var NOT_THIS_ACCOUNT_SENTENCE = `Halfcycle is not connected: this project belongs to another Halfcycle account. To switch, run "npx halfcycle" in this project and ${SWITCH_ACCOUNT_ANSWER}.`;
18863
- var UPDATE_CLAUDE_CODE_SENTENCE = "Halfcycle is not connected: this Claude Code version does not name the server it connects to, so no credential was sent. Update Claude Code, then reopen.";
18864
- var CANNOT_REACH_SENTENCE = "Halfcycle is not connected: its server could not be reached, so its tools are unavailable in this session. Check your network, then reopen the project.";
18865
- var SERVICE_PROBLEM_SENTENCE = "Halfcycle is not connected: its service had a problem just now, so nothing in this session is being checked. Reopen the project to try again.";
18866
- var FOLDER_NOT_TRUSTED_SENTENCE = "Folder not yet trusted. Open this folder once in interactive Claude Code and trust it; then headless runs work.";
18867
- var SCOPE_UNCHECKED_SENTENCE = "If this project needs approval before building, that cannot be checked either, so work done now shows as built without approval unless already approved.";
18868
- var PLACEHOLDER_SENTENCES = Object.freeze({
18869
- [NO_CREDENTIAL_PLACEHOLDER]: NO_CREDENTIAL_SENTENCE,
18870
- [NOT_THIS_ACCOUNT_PLACEHOLDER]: NOT_THIS_ACCOUNT_SENTENCE,
18871
- [UPDATE_CLAUDE_CODE_PLACEHOLDER]: UPDATE_CLAUDE_CODE_SENTENCE
18872
- });
18873
-
18874
- // ../events/dist/scope-chat.js
18875
- var chatScopeApproveRequestSchema = external_exports.object({
18876
- act: external_exports.literal("approve"),
18877
- reference: phaseIdentitySchema,
18878
- planVersion: planVersionSchema
18860
+ var PACE_NOT_PACED_REASONS = ["not-configured", "unreadable"];
18861
+ var limitHeldSchema = external_exports.object({
18862
+ used: external_exports.number().int().nonnegative(),
18863
+ limit: external_exports.number().int().positive(),
18864
+ raiseBy: external_exports.number().int().nonnegative(),
18865
+ working: external_exports.number().int().nonnegative(),
18866
+ weekCeilingTokens: external_exports.number().int().positive().optional()
18879
18867
  }).strict();
18880
- var chatScopeSendBackRequestSchema = external_exports.object({
18881
- act: external_exports.literal("send-back"),
18882
- reference: phaseIdentitySchema,
18883
- planVersion: planVersionSchema,
18884
- note: scopeNoteWireSchema.optional()
18868
+ var paceUsageSchema = external_exports.object({
18869
+ used: external_exports.number().int().nonnegative(),
18870
+ limit: external_exports.number().int().positive(),
18871
+ weekCeilingTokens: external_exports.number().int().positive().optional()
18885
18872
  }).strict();
18886
- var chatScopeRequestSchema = external_exports.discriminatedUnion("act", [
18887
- chatScopeApproveRequestSchema,
18888
- chatScopeSendBackRequestSchema
18873
+ var paceRunResultSchema = external_exports.discriminatedUnion("outcome", [
18874
+ external_exports.object({
18875
+ outcome: external_exports.literal("paced"),
18876
+ pace: paceStateSchema,
18877
+ number: external_exports.number().int().min(1).max(PACE_AGENTS_MAX),
18878
+ allowedNow: external_exports.number().int().nonnegative(),
18879
+ windowKind: paceWindowKindSchema.optional(),
18880
+ until: utcIso8601.optional(),
18881
+ keepFreePercent: keepFreePercentSchema.optional(),
18882
+ suggestion: paceSuggestionSchema.optional(),
18883
+ limit: external_exports.literal("reached").optional(),
18884
+ limitHeld: limitHeldSchema.optional(),
18885
+ usage: paceUsageSchema.optional()
18886
+ }).strict(),
18887
+ external_exports.object({ outcome: external_exports.literal("not-paced"), reason: external_exports.enum(PACE_NOT_PACED_REASONS) }).strict()
18889
18888
  ]);
18890
- var CHAT_SCOPE_ACTS = SCOPE_ACTS;
18891
- var chatScopeRecordedSchema = external_exports.object({
18892
- outcome: external_exports.literal("recorded"),
18893
- act: external_exports.enum(CHAT_SCOPE_ACTS),
18894
- phase: scopePhaseSchema,
18895
- planVersion: planVersionSchema
18896
- }).strict();
18897
- var chatScopeAlreadyApprovedSchema = external_exports.object({
18898
- outcome: external_exports.literal("already-approved"),
18899
- replanNeeded: external_exports.boolean()
18900
- }).strict();
18901
- var chatScopeRefusedSchema = external_exports.object({
18902
- outcome: external_exports.literal("refused"),
18903
- problems: external_exports.array(systemDescriptionProblemSchema).min(1)
18904
- }).strict();
18905
- var chatAmbiguousSchema = external_exports.object({
18906
- outcome: external_exports.literal("ambiguous"),
18907
- phases: external_exports.array(scopePhaseSchema).min(2)
18908
- }).strict();
18909
- function bareOutcome(outcome) {
18910
- return external_exports.object({ outcome: external_exports.literal(outcome) }).strict();
18889
+ function tokenCountWordsOf(n) {
18890
+ if (n < 1e3)
18891
+ return String(Math.max(0, Math.round(n)));
18892
+ const rounded = Number(n.toPrecision(2));
18893
+ const [unit, word] = rounded >= 1e9 ? [1e9, "billion"] : rounded >= 1e6 ? [1e6, "million"] : [1e3, "thousand"];
18894
+ return `${String(Number((rounded / unit).toPrecision(2)))} ${word}`;
18911
18895
  }
18912
- var chatScopeResultSchema = external_exports.discriminatedUnion("outcome", [
18913
- chatScopeRecordedSchema,
18914
- chatScopeAlreadyApprovedSchema,
18915
- chatScopeRefusedSchema,
18916
- chatAmbiguousSchema,
18917
- bareOutcome("show-first"),
18918
- bareOutcome("plan-changed"),
18919
- bareOutcome("build-started"),
18920
- bareOutcome("not-owner"),
18921
- bareOutcome("no-owner"),
18922
- bareOutcome("not-offered"),
18923
- bareOutcome("unavailable")
18924
- ]);
18925
-
18926
- // ../events/dist/review-chat.js
18927
- var chatReviewAcceptRequestSchema = external_exports.object({
18928
- act: external_exports.literal("accept"),
18929
- reference: phaseIdentitySchema,
18930
- reviewVersion: external_exports.number().int().min(0),
18931
- parts: external_exports.array(systemPartIdSchema).max(REVIEW_PARTS_MAX),
18932
- waitingClaims: external_exports.literal(true).optional()
18933
- }).strict();
18934
- function notedRequest(act) {
18935
- return external_exports.object({
18936
- act: external_exports.literal(act),
18937
- reference: phaseIdentitySchema,
18938
- reviewVersion: external_exports.number().int().min(0),
18939
- part: systemPartIdSchema,
18940
- note: reviewNoteWireSchema
18941
- }).strict();
18896
+ function paceAgentsOf(n) {
18897
+ return n === 1 ? "1 agent" : `${String(n)} agents`;
18942
18898
  }
18943
- var chatReviewQuestionRequestSchema = notedRequest("question");
18944
- var chatReviewSendBackRequestSchema = notedRequest("send-back");
18945
- var chatReviewCarryRequestSchema = notedRequest("carry");
18946
- var chatReviewDropRequestSchema = notedRequest("drop");
18947
- var chatReviewRejectRequestSchema = external_exports.object({
18948
- act: external_exports.literal("reject"),
18949
- reference: phaseIdentitySchema,
18950
- reviewVersion: external_exports.number().int().min(0),
18951
- task: buildTaskIdSchema,
18952
- note: reviewNoteWireSchema
18899
+ var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
18900
+ var MONTH_NAMES = [
18901
+ "January",
18902
+ "February",
18903
+ "March",
18904
+ "April",
18905
+ "May",
18906
+ "June",
18907
+ "July",
18908
+ "August",
18909
+ "September",
18910
+ "October",
18911
+ "November",
18912
+ "December"
18913
+ ];
18914
+ var SIX_DAYS_MS = 6 * 24 * 60 * 60 * 1e3;
18915
+ function paceClockOf(at, now) {
18916
+ const hours = at.getHours();
18917
+ const clock = `${String(hours % 12 === 0 ? 12 : hours % 12)}:${String(at.getMinutes()).padStart(2, "0")} ${hours < 12 ? "am" : "pm"}`;
18918
+ const sameDay = at.getFullYear() === now.getFullYear() && at.getMonth() === now.getMonth() && at.getDate() === now.getDate();
18919
+ if (sameDay)
18920
+ return clock;
18921
+ const day = DAY_NAMES[at.getDay()] ?? "";
18922
+ if (at.getTime() - now.getTime() > SIX_DAYS_MS) {
18923
+ return `${day} ${String(at.getDate())} ${MONTH_NAMES[at.getMonth()] ?? ""} ${clock}`;
18924
+ }
18925
+ return `${day} ${clock}`;
18926
+ }
18927
+ var PACE_P5 = "This build may use all of your allowance. It will not slow down to leave room for you.";
18928
+ function paceS1Of(window, k, until, now, keepFreePercent) {
18929
+ return `Halfcycle is slowing this build so you keep about ${paceShareWordsOf(keepFreePercent)} of this ${paceWindowWordsOf(window)} for your own work. It will run at most ${paceAgentsOf(k)} at once until ${paceClockOf(until, now)}.`;
18930
+ }
18931
+ function paceS2Of(window, k, until, now, keepFreePercent) {
18932
+ const head = `Halfcycle has paused new work in this build until ${paceClockOf(until, now)}, when your ${paceWindowWordsOf(window)} resets, so you keep about ${paceShareWordsOf(keepFreePercent)} of it for your own work.`;
18933
+ if (k === 0)
18934
+ return head;
18935
+ return k === 1 ? `${head} The 1 agent already working will finish its task.` : `${head} The ${String(k)} agents already working will finish their tasks.`;
18936
+ }
18937
+ function paceS3Of(window, n) {
18938
+ return `Your ${paceWindowWordsOf(window)} has reset. Halfcycle is starting new work again, up to ${paceAgentsOf(n)} at once.`;
18939
+ }
18940
+ function paceOfferTextOf(input) {
18941
+ const canFaster = input.number < PACE_AGENTS_MAX;
18942
+ const canUseMore = input.keepFreePercent > 0;
18943
+ if (!canFaster && !canUseMore)
18944
+ return null;
18945
+ const head = `You have used only ${String(Math.floor(input.percentUsed))}% of this ${paceWindowWordsOf(input.window)}, and more than half of it has passed.`;
18946
+ const useUpTo = `"use up to ${String(Math.min(100, 100 - input.keepFreePercent + 10))}%" to let this build use more of it`;
18947
+ if (canFaster && canUseMore)
18948
+ return `${head} Say "faster" for one more agent, or ${useUpTo}.`;
18949
+ if (canFaster)
18950
+ return `${head} Say "faster" for one more agent.`;
18951
+ return `${head} Say ${useUpTo}.`;
18952
+ }
18953
+ function paceF1Of(k) {
18954
+ return `This build runs at most ${paceAgentsOf(k)} at once.`;
18955
+ }
18956
+ function paceAgentLineOf(pace, allowance, until, now) {
18957
+ const fewer = `once fewer than ${paceAgentsOf(allowance)} are working.`;
18958
+ if (until === null)
18959
+ return `Start it ${fewer}`;
18960
+ if (pace === "holding")
18961
+ return `Start it after ${paceClockOf(until, now)}.`;
18962
+ return `Start it after ${paceClockOf(until, now)}, or ${fewer}`;
18963
+ }
18964
+ var PACE_NOTHING_CHANGED = "Nothing was changed.";
18965
+ function paceHeldStartTextOf(input) {
18966
+ const { pace, allowance, working, window, until, now, keepFreePercent } = input;
18967
+ if (pace === "full" || window === null || until === null) {
18968
+ return `${paceF1Of(allowance)} ${paceAgentLineOf("full", allowance, null, now)} ${PACE_NOTHING_CHANGED}`;
18969
+ }
18970
+ const head = pace === "slowing" ? paceS1Of(window, allowance, until, now, keepFreePercent) : paceS2Of(window, working, until, now, keepFreePercent);
18971
+ return `${head} ${paceAgentLineOf(pace, allowance, until, now)} ${PACE_NOTHING_CHANGED}`;
18972
+ }
18973
+ var PACE_FIRST_READ = "Reading this computer's usage records for the first time. This can take a minute.";
18974
+ var PACE_AGENTS_TOO_MANY = `Run at most ${String(PACE_AGENTS_MAX)} agents at once.`;
18975
+ var PACE_ONLY_PERSON_RAISES = 'Only the person can raise the number of agents, by typing "faster" or "run 5 agents" in the chat. Nothing was changed.';
18976
+ function paceStatusLineOf(percent, window) {
18977
+ return `Halfcycle \xB7 ${String(Math.round(percent))}% of this ${paceWindowWordsOf(window)} used`;
18978
+ }
18979
+ var STARTING_TOKENS_PER_TASK = 9e6;
18980
+ var LIMIT_MULTIPLE_DEFAULT = 2;
18981
+ var LIMIT_MULTIPLE_RANGE = { min: 1, max: 20 };
18982
+ var LIMIT_FIGURE_RANGE = { min: 1e6, max: 1e11 };
18983
+ var LIMIT_HISTORY_MAX = 20;
18984
+ var LIMIT_ROUND_TOKENS = 1e3;
18985
+ var LIMIT_COUNTED_KINDS = ["build", "review", "check"];
18986
+ var LIMIT_PARTS = ["half", "three-quarters", "nine-tenths", "limit"];
18987
+ var limitPartSchema = external_exports.enum(LIMIT_PARTS);
18988
+ var LIMIT_PART_SHARE = {
18989
+ half: 0.5,
18990
+ "three-quarters": 0.75,
18991
+ "nine-tenths": 0.9,
18992
+ limit: 1
18993
+ };
18994
+ var LIMIT_SET_BY = ["default", "person", "other"];
18995
+ var limitSetBySchema = external_exports.enum(LIMIT_SET_BY);
18996
+ var LIMIT_TYPED_BY = ["person", "not-attended", "cannot-tell"];
18997
+ var limitTypedBySchema = external_exports.enum(LIMIT_TYPED_BY);
18998
+ var limitHistoryEntrySchema = external_exports.object({
18999
+ at: utcIso8601,
19000
+ kind: external_exports.enum(["raised", "set", "stopped"]),
19001
+ limit: external_exports.number().int().positive(),
19002
+ by: limitTypedBySchema,
19003
+ relayed: external_exports.literal(true).optional()
18953
19004
  }).strict();
18954
- var chatReviewRequestSchema = external_exports.discriminatedUnion("act", [
18955
- chatReviewAcceptRequestSchema,
18956
- chatReviewQuestionRequestSchema,
18957
- chatReviewSendBackRequestSchema,
18958
- chatReviewCarryRequestSchema,
18959
- chatReviewDropRequestSchema,
18960
- chatReviewRejectRequestSchema
18961
- ]);
18962
- function bareOutcome2(outcome) {
18963
- return external_exports.object({ outcome: external_exports.literal(outcome) }).strict();
19005
+ var PHASE_LIMIT_FILENAME = "limit.json";
19006
+ var phaseLimitFileSchema = external_exports.object({
19007
+ phase: phaseNameSchema,
19008
+ expected: external_exports.number().int().nonnegative(),
19009
+ expectedMinutes: external_exports.number().nonnegative().nullable(),
19010
+ limit: external_exports.number().int().positive(),
19011
+ multiple: external_exports.number().positive().nullable(),
19012
+ setBy: limitSetBySchema,
19013
+ firstGuess: external_exports.boolean(),
19014
+ shown: external_exports.array(limitPartSchema).max(LIMIT_PARTS.length),
19015
+ awaiting: external_exports.boolean(),
19016
+ history: external_exports.array(limitHistoryEntrySchema).max(LIMIT_HISTORY_MAX)
19017
+ }).strict();
19018
+ function phaseLimitFileOf(raw, phase) {
19019
+ const parsed = phaseLimitFileSchema.safeParse(raw);
19020
+ if (!parsed.success)
19021
+ return null;
19022
+ return phase === null || parsed.data.phase === phase ? parsed.data : null;
19023
+ }
19024
+ var phaseUsageFileSchema = external_exports.object({
19025
+ phase: phaseNameSchema,
19026
+ openedAt: utcIso8601,
19027
+ tokens: external_exports.number().int().nonnegative(),
19028
+ at: utcIso8601
19029
+ }).strict();
19030
+ function limitPartLineOf(part, used, weekCeilingTokens = null) {
19031
+ return limitPartShareTextOf(part, used, weekCeilingTokens);
18964
19032
  }
18965
- function notTheConsoleNotDecidable(option) {
18966
- return option !== reviewNotDecidableSchema;
19033
+ function limitReachedPartsOf(input) {
19034
+ const { figure, caveat } = limitReachedFigureOf(input.limit, input.weekCeilingTokens);
19035
+ const finish = input.working === 0 ? "" : input.working === 1 ? " The 1 agent already working will finish its task." : ` The ${String(input.working)} agents already working will finish their tasks.`;
19036
+ return {
19037
+ happened: `This phase has reached its usage limit${figure}. No new work will start.${finish}`,
19038
+ yourPart: 'Say "keep going" to raise the limit and carry on, or "stop" to leave the phase paused here.',
19039
+ next: `Nothing is lost either way; the work waits for your answer.${caveat}`
19040
+ };
18967
19041
  }
18968
- var chatReviewResultSchema = external_exports.discriminatedUnion("outcome", [
18969
- reviewPartsNotDecidableSchema,
18970
- ...reviewDecisionResultSchema.options.filter(notTheConsoleNotDecidable),
18971
- bareOutcome2("show-first"),
18972
- bareOutcome2("not-offered"),
18973
- chatAmbiguousSchema,
18974
- bareOutcome2("nothing-waiting")
18975
- ]);
18976
- var reviewAnswerRequestSchema = external_exports.object({
18977
- part: systemPartIdSchema,
18978
- answer: statementText(REVIEW_ANSWER_MAX)
19042
+ function limitReachedLineOf(input) {
19043
+ return stopTextOf(limitReachedPartsOf(input));
19044
+ }
19045
+ function limitHeldStartTextOf(limit, weekCeilingTokens = null) {
19046
+ return `${limitHeldShareTextOf(limit, weekCeilingTokens)} Ask the person to say "keep going" or "stop", and start nothing until they have. ${PACE_NOTHING_CHANGED}`;
19047
+ }
19048
+
19049
+ // ../events/dist/plan-usage.js
19050
+ var windowPercentSchema = external_exports.number().min(0).max(100);
19051
+ var planUsageWindowSchema = external_exports.object({
19052
+ usedPercent: windowPercentSchema,
19053
+ resetsAt: utcIso8601
18979
19054
  }).strict();
18980
- var reviewAnswerRecordedSchema = external_exports.object({
18981
- outcome: external_exports.literal("recorded"),
18982
- name: systemPartSchema.shape.name
19055
+ var planUsageWeekSchema = planUsageWindowSchema.extend({
19056
+ ceilingTokens: external_exports.number().int().positive().nullable()
18983
19057
  }).strict();
18984
- var reviewAnswerResultSchema = external_exports.discriminatedUnion("outcome", [
18985
- reviewAnswerRecordedSchema,
18986
- bareOutcome2("no-question"),
18987
- reviewRefusedSchema,
18988
- bareOutcome2("unavailable")
18989
- ]);
18990
-
18991
- // ../events/dist/owner-waits.js
18992
- var OWNER_WAIT_ITEMS = Object.freeze([
18993
- Object.freeze({ id: "delete-users-data", words: "Deleting or replacing data people already have." }),
18994
- Object.freeze({
18995
- id: "change-promises",
18996
- words: "Changing what the product promises its users, such as pricing, terms or privacy."
18997
- }),
18998
- Object.freeze({ id: "spend-or-sign-up", words: "Anything that costs money or signs up for a service." }),
18999
- Object.freeze({ id: "publish-or-release", words: "Publishing or releasing to users." }),
19000
- Object.freeze({ id: "reshape-shared-data", words: "Dropping or reshaping stored data that other parts read." }),
19001
- Object.freeze({ id: "change-standing-rule", words: "Changing a standing rule." })
19002
- ]);
19003
- var OWNER_WAIT_ITEM_IDS = Object.freeze(OWNER_WAIT_ITEMS.map((item) => item.id));
19004
- var ownerWaitItemIdSchema = external_exports.enum(OWNER_WAIT_ITEM_IDS);
19005
- var OWNER_WAIT_FIXED_COUNT = OWNER_WAIT_ITEMS.length;
19006
- var OWNER_WAIT_ADDITIONS_MAX = 20;
19007
- var OWNER_WAIT_TEXT_MAX = 240;
19008
- var ownerWaitItemRefSchema = external_exports.union([
19009
- external_exports.number().int().min(1),
19010
- external_exports.string().uuid({ message: "must be a number from 1 to 6, or the id shown beside an added item" })
19011
- ]);
19012
- var ownerWaitText = descriptionText(OWNER_WAIT_TEXT_MAX);
19013
- var ownerWaitAddRequestSchema = external_exports.object({
19014
- engagementId: external_exports.string().min(1),
19015
- text: ownerWaitText,
19016
- reason: ownerWaitText
19058
+ var planUsageReportSchema = external_exports.object({
19059
+ fiveHour: planUsageWindowSchema.nullable(),
19060
+ week: planUsageWeekSchema.nullable(),
19061
+ readAt: utcIso8601
19017
19062
  }).strict();
19018
- var ownerWaitAddResultSchema = external_exports.discriminatedUnion("outcome", [
19019
- external_exports.object({ outcome: external_exports.literal("added"), id: external_exports.string().uuid(), addedAt: utcIso8601 }).strict(),
19020
- external_exports.object({ outcome: external_exports.literal("refused"), reason: external_exports.literal("too-many") }).strict()
19021
- ]);
19022
- var ownerWaitUndoableSchema = external_exports.enum(["yes", "no"]);
19023
- var ownerWaitDeclareRequestSchema = external_exports.object({
19024
- engagementId: external_exports.string().min(1),
19025
- item: ownerWaitItemRefSchema,
19026
- question: ownerWaitText,
19027
- recommendation: ownerWaitText,
19028
- task: buildTaskIdSchema.optional(),
19029
- by: agentNameSchema.optional(),
19030
- undoable: ownerWaitUndoableSchema.optional(),
19031
- how: statementText(STATEMENT_MAX).optional()
19032
- }).strict().refine((r) => r.undoable === "yes" === (r.how !== void 0), {
19033
- message: "says how to undo it exactly when it can be undone",
19034
- path: ["how"]
19035
- });
19036
- var OWNER_WAIT_DECLARE_REFUSALS = ["no-open-phase", "unknown-item", "say-if-undoable"];
19037
- var ownerWaitDeclareResultSchema = external_exports.discriminatedUnion("outcome", [
19038
- external_exports.object({ outcome: external_exports.literal("recorded"), id: external_exports.string().uuid(), say: external_exports.string().min(1) }).strict(),
19039
- external_exports.object({
19040
- outcome: external_exports.literal("go-ahead"),
19041
- id: external_exports.string().uuid(),
19042
- callId: external_exports.number().int().positive(),
19043
- say: external_exports.string().min(1)
19044
- }).strict(),
19045
- external_exports.object({ outcome: external_exports.literal("refused"), reason: external_exports.enum(OWNER_WAIT_DECLARE_REFUSALS) }).strict()
19046
- ]);
19047
- var ownerWaitAddedItemSchema = external_exports.object({
19048
- id: external_exports.string().uuid(),
19049
- words: external_exports.string().min(1),
19050
- reason: external_exports.string().min(1),
19051
- addedAt: utcIso8601
19063
+ var planKeepFreePercentSchema = external_exports.number().int().min(0).max(90);
19064
+ var workMinutesSchema = external_exports.number().int().nonnegative().nullable();
19065
+ var planUsagePhaseSchema = external_exports.object({
19066
+ phase: phaseNameSchema,
19067
+ spentPercent: timeSharePercentSchema,
19068
+ expectedPercent: timeSharePercentSchema.optional(),
19069
+ kinds: timeShareKindsSchema
19052
19070
  }).strict();
19053
- var ownerWaitListSchema = external_exports.object({
19054
- fixed: external_exports.array(external_exports.object({ id: ownerWaitItemIdSchema, words: external_exports.string().min(1) }).strict()).length(OWNER_WAIT_FIXED_COUNT),
19055
- added: external_exports.array(ownerWaitAddedItemSchema).max(OWNER_WAIT_ADDITIONS_MAX)
19071
+ var PLAN_USAGE_PHASES_MAX = 100;
19072
+ var planUsageUnknownSchema = external_exports.object({
19073
+ state: external_exports.literal("unknown"),
19074
+ windowsRead: external_exports.boolean(),
19075
+ workMinutes: workMinutesSchema,
19076
+ keepFreePercent: planKeepFreePercentSchema
19056
19077
  }).strict();
19057
- var ownerWaitDeclarationSchema = external_exports.object({
19058
- id: external_exports.string().uuid(),
19059
- fixedItem: ownerWaitItemIdSchema.nullable(),
19060
- addition: external_exports.object({ id: external_exports.string().uuid(), words: external_exports.string().min(1) }).strict().nullable(),
19061
- question: external_exports.string().min(1),
19062
- recommendation: external_exports.string().min(1),
19063
- task: buildTaskIdSchema.nullable(),
19064
- declaredAt: utcIso8601
19065
- }).strict().refine((d) => d.fixedItem === null !== (d.addition === null), {
19066
- message: "names exactly one item: a fixed one or an added one",
19067
- path: ["fixedItem"]
19078
+ var planUsageKnownSchema = external_exports.object({
19079
+ state: external_exports.literal("known"),
19080
+ workMinutes: workMinutesSchema,
19081
+ keepFreePercent: planKeepFreePercentSchema,
19082
+ weekCeilingTokens: external_exports.number().int().positive(),
19083
+ fiveHourPercent: windowPercentSchema.nullable(),
19084
+ weekPercent: windowPercentSchema.nullable(),
19085
+ readAt: utcIso8601,
19086
+ phases: external_exports.array(planUsagePhaseSchema).max(PLAN_USAGE_PHASES_MAX)
19087
+ }).strict().superRefine((v, ctx) => {
19088
+ const seen = /* @__PURE__ */ new Set();
19089
+ v.phases.forEach((entry, i) => {
19090
+ if (seen.has(entry.phase))
19091
+ ctx.addIssue({ code: "custom", path: ["phases", i, "phase"], message: "lists one phase twice" });
19092
+ seen.add(entry.phase);
19093
+ });
19068
19094
  });
19069
- var ownerWaitDeclarationsSchema = external_exports.object({ declarations: external_exports.array(ownerWaitDeclarationSchema) }).strict();
19070
- var OWNER_WAIT_TOO_MANY = `This project already has ${String(OWNER_WAIT_ADDITIONS_MAX)} things added to what waits for the owner. Ask the owner to take one off on their settings page in the console first; it cannot be done by typing in chat.`;
19071
-
19072
- // ../events/dist/owner-wait-answer.js
19073
- var OWNER_WAIT_SHORT_ID_LENGTH = 8;
19074
- var ownerWaitShortIdSchema = external_exports.string().regex(/^[0-9a-f]{8}$/);
19075
- function ownerWaitShortIdOf(declarationId) {
19076
- return declarationId.slice(0, OWNER_WAIT_SHORT_ID_LENGTH).toLowerCase();
19095
+ var planUsageSchema = external_exports.discriminatedUnion("state", [planUsageUnknownSchema, planUsageKnownSchema]);
19096
+ function agentWorkMinutesOf(tasks, medianTaskMinutes) {
19097
+ if (tasks === null || tasks.length === 0)
19098
+ return null;
19099
+ const sum2 = tasks.reduce((total, task) => total + taskMinutesOf(task, medianTaskMinutes), 0);
19100
+ return paceDurationRoundedOf(sum2);
19077
19101
  }
19078
- var OWNER_WAIT_ANSWER_ACTS = ["approve", "decline"];
19079
- var ownerWaitAnswerActSchema = external_exports.enum(OWNER_WAIT_ANSWER_ACTS);
19080
- function ownerWaitApproveLineOf(declarationId) {
19081
- return `approve wait ${ownerWaitShortIdOf(declarationId)}`;
19102
+ function weekPercentOf(tokens, weekCeilingTokens) {
19103
+ return weekCeilingTokens > 0 ? tokens / weekCeilingTokens * 100 : 0;
19082
19104
  }
19083
- function ownerWaitDeclineLineOf(declarationId) {
19084
- return `decline wait ${ownerWaitShortIdOf(declarationId)}: <why>`;
19105
+ function planSharePercentWordsOf(percent) {
19106
+ return percent < 0.5 ? "under 1%" : `${String(Math.round(percent))}%`;
19085
19107
  }
19086
- var ownerWaitChatRequestSchema = external_exports.discriminatedUnion("act", [
19087
- external_exports.object({ act: external_exports.literal("approve"), wait: ownerWaitShortIdSchema }).strict(),
19088
- external_exports.object({ act: external_exports.literal("decline"), wait: ownerWaitShortIdSchema, note: scopeNoteWireSchema }).strict()
19089
- ]);
19090
- function bareOutcome3(outcome) {
19091
- return external_exports.object({ outcome: external_exports.literal(outcome) }).strict();
19108
+ function aboutShareOf(percent) {
19109
+ return percent < 0.5 ? planSharePercentWordsOf(percent) : `about ${planSharePercentWordsOf(percent)}`;
19092
19110
  }
19093
- var ownerWaitChatResultSchema = external_exports.discriminatedUnion("outcome", [
19094
- external_exports.object({
19095
- outcome: external_exports.literal("recorded"),
19096
- act: ownerWaitAnswerActSchema,
19097
- question: external_exports.string().min(1)
19098
- }).strict(),
19099
- external_exports.object({ outcome: external_exports.literal("refused"), problems: external_exports.array(systemDescriptionProblemSchema).min(1) }).strict(),
19100
- bareOutcome3("not-owner"),
19101
- bareOutcome3("no-owner"),
19102
- bareOutcome3("not-found"),
19103
- bareOutcome3("ambiguous"),
19104
- bareOutcome3("not-offered"),
19105
- bareOutcome3("unavailable")
19111
+ var PLAN_USAGE_CAVEAT = "These figures are estimates. Many things affect usage, and some of them are outside Halfcycle's control.";
19112
+ function ofYourWeek(tokens, weekCeilingTokens) {
19113
+ return `${aboutShareOf(weekPercentOf(tokens, weekCeilingTokens))} of your week`;
19114
+ }
19115
+ function ceilingKnown(weekCeilingTokens) {
19116
+ return typeof weekCeilingTokens === "number" && weekCeilingTokens > 0;
19117
+ }
19118
+ function figureThenCaveat(sentence) {
19119
+ return `${sentence} ${PLAN_USAGE_CAVEAT}`;
19120
+ }
19121
+ function limitPartShareTextOf(part, used, weekCeilingTokens) {
19122
+ const words = part === "half" ? "half" : part === "three-quarters" ? "three quarters" : "nine tenths";
19123
+ const head = ceilingKnown(weekCeilingTokens) ? figureThenCaveat(`This phase has used ${words} of its usage limit: ${ofYourWeek(used, weekCeilingTokens)}.`) : `This phase has used ${words} of its usage limit.`;
19124
+ return part === "nine-tenths" ? `${head} New work stops at the limit.` : head;
19125
+ }
19126
+ function limitReachedFigureOf(limit, weekCeilingTokens) {
19127
+ if (!ceilingKnown(weekCeilingTokens))
19128
+ return { figure: "", caveat: "" };
19129
+ return { figure: `: ${ofYourWeek(limit, weekCeilingTokens)}`, caveat: ` ${PLAN_USAGE_CAVEAT}` };
19130
+ }
19131
+ function limitKeepGoingShareText(limit, used, weekCeilingTokens) {
19132
+ if (!ceilingKnown(weekCeilingTokens))
19133
+ return "Done. The limit is raised. New work can start again.";
19134
+ return `${figureThenCaveat(`Done. The limit is now ${ofYourWeek(limit, weekCeilingTokens)}.`)} This phase has used ${aboutShareOf(weekPercentOf(used, weekCeilingTokens))}. New work can start again.`;
19135
+ }
19136
+ function limitAlreadyUsedShareSuffix(limit, used, weekCeilingTokens) {
19137
+ if (limit > used)
19138
+ return "";
19139
+ return ceilingKnown(weekCeilingTokens) ? ` This phase has already used ${ofYourWeek(used, weekCeilingTokens)}, so no new work will start until you raise it.` : " This phase has already used that much, so no new work will start until you raise it.";
19140
+ }
19141
+ function limitTimesShareText(limit, typed, used, weekCeilingTokens) {
19142
+ const suffix = limitAlreadyUsedShareSuffix(limit, used, weekCeilingTokens);
19143
+ if (!ceilingKnown(weekCeilingTokens)) {
19144
+ return `Done. The limit for this phase is now ${typed} times what it is expected to use.${suffix}`;
19145
+ }
19146
+ return `${figureThenCaveat(`Done. The limit for this phase is now ${ofYourWeek(limit, weekCeilingTokens)}.`)} That is ${typed} times what it is expected to use.${suffix}`;
19147
+ }
19148
+ function limitFigureShareText(limit, used, weekCeilingTokens) {
19149
+ const suffix = limitAlreadyUsedShareSuffix(limit, used, weekCeilingTokens);
19150
+ if (!ceilingKnown(weekCeilingTokens))
19151
+ return `Done. The limit for this phase is set.${suffix}`;
19152
+ return `${figureThenCaveat(`Done. The limit for this phase is now ${ofYourWeek(limit, weekCeilingTokens)}.`)}${suffix}`;
19153
+ }
19154
+ function limitHeldShareTextOf(limit, weekCeilingTokens) {
19155
+ if (!ceilingKnown(weekCeilingTokens))
19156
+ return "This phase has reached its usage limit, so no new agent starts.";
19157
+ return figureThenCaveat(`This phase has reached its usage limit of ${ofYourWeek(limit, weekCeilingTokens)}, so no new agent starts.`);
19158
+ }
19159
+ function limitNoExpectedShareText(weekCeilingTokens) {
19160
+ return ceilingKnown(weekCeilingTokens) ? 'Nothing changed: there is no expected usage yet, so say a share of the week, like "limit it to 10% of the week".' : "Nothing changed: there is no expected usage for this phase yet.";
19161
+ }
19162
+ function limitTimesRangeShareText(range, weekCeilingTokens) {
19163
+ const head = `Nothing changed: a limit is between ${String(range.min)} and ${String(range.max)} times what is expected.`;
19164
+ return ceilingKnown(weekCeilingTokens) ? `${head} Say a share of the week instead, like "limit it to 10% of the week".` : head;
19165
+ }
19166
+ var LIMIT_SHARE_NOT_SET_TEXT = 'Nothing changed. To set a limit now, say "limit it to 2 times".';
19167
+
19168
+ // ../events/dist/client-version.js
19169
+ var CLIENT_VERSION_HEADER = "x-halfcycle-client";
19170
+ var TYPED_APPROVAL_OFF_SENTENCE = "Approving by typing is switched off for now.";
19171
+ var HEADLESS_CANNOT_APPROVE = "(a headless session cannot approve, by design)";
19172
+ var TYPED_REVIEW_OFF_SENTENCE = "Reviewing by typing is switched off for now.";
19173
+ var CLIENT_UPDATE_SENTENCE = `To approve by typing in a session, Halfcycle needs updating: run \`${HALFCYCLE_COMMAND}\` in this project's folder, then reopen your session and ask again.`;
19174
+
19175
+ // ../events/dist/device-auth.js
19176
+ var utcIso86012 = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/, "must be a UTC ISO-8601 timestamp ending in Z");
19177
+ var DEVICE_AUTH_STATUS = {
19178
+ /** The poll: minted, nobody has acted yet. Keep polling. 200. */
19179
+ PENDING: "pending",
19180
+ /** The poll: approved, and the credential is in this body. Once only. 200. */
19181
+ APPROVED: "approved",
19182
+ /** Confirm: the decision was recorded (approve or decline). 200. */
19183
+ RECORDED: "recorded",
19184
+ /** The human refused at the confirmation page. 400 on the poll. */
19185
+ DECLINED: "declined",
19186
+ /** The code's absolute expiry has passed. 400. */
19187
+ EXPIRED: "expired",
19188
+ /** Never minted, or already spent. 400. */
19189
+ UNKNOWN: "unknown",
19190
+ /** Confirm: neither `approve` nor `decline` was named. Nothing was bound. 400. */
19191
+ INVALID_DECISION: "invalid-decision",
19192
+ /** Confirm: an approval arrived with no signed-in identity. 400. */
19193
+ IDENTITY_REQUIRED: "identity-required",
19194
+ /** This deployment has no `SIGNIN_BASE_URL` / `DEVICE_AUTH_CONFIRM_SECRET`. 503. */
19195
+ NOT_CONFIGURED: "not-configured",
19196
+ /** Confirm: the shared confirmation secret was missing or wrong. 401. */
19197
+ UNAUTHORIZED: "unauthorized",
19198
+ /** Confirm: the code was already approved, declined or claimed. 409. */
19199
+ ALREADY_DECIDED: "already-decided",
19200
+ /**
19201
+ * Confirm: an approval named a signed-in identity whose account has not accepted
19202
+ * the terms of service and privacy policy currently in force. 400.
19203
+ *
19204
+ * NOT BOUND TO THE DEVICE CODE. Unlike every other refusal in this const, this one
19205
+ * says nothing about the code itself — the code is still `pending` after this
19206
+ * response, exactly as it was before the request, so the SAME userCode can be
19207
+ * confirmed again once the caller has recorded acceptance. `deviceConfirmRequestSchema`'s
19208
+ * `acceptTerms` is how a second confirm says it did.
19209
+ */
19210
+ TERMS_REQUIRED: "terms-required"
19211
+ };
19212
+ var DEVICE_POLL_REFUSALS = [
19213
+ DEVICE_AUTH_STATUS.DECLINED,
19214
+ DEVICE_AUTH_STATUS.EXPIRED,
19215
+ DEVICE_AUTH_STATUS.UNKNOWN
19216
+ ];
19217
+ var deviceAuthStartResponseSchema = external_exports.object({
19218
+ deviceCode: external_exports.string().min(1),
19219
+ userCode: external_exports.string().min(1),
19220
+ verificationUrl: external_exports.string().min(1),
19221
+ expiresAt: utcIso86012,
19222
+ pollIntervalMs: external_exports.number().int().positive()
19223
+ }).strict();
19224
+ var devicePollPendingSchema = external_exports.object({
19225
+ status: external_exports.literal(DEVICE_AUTH_STATUS.PENDING),
19226
+ pollIntervalMs: external_exports.number().int().positive()
19227
+ }).strict();
19228
+ var devicePollApprovedSchema = external_exports.object({
19229
+ status: external_exports.literal(DEVICE_AUTH_STATUS.APPROVED),
19230
+ accountId: external_exports.string().min(1),
19231
+ credential: external_exports.string().min(1),
19232
+ expiresAt: utcIso86012
19233
+ }).strict();
19234
+ var deviceAuthRefusalSchema = external_exports.object({
19235
+ status: external_exports.enum([
19236
+ DEVICE_AUTH_STATUS.DECLINED,
19237
+ DEVICE_AUTH_STATUS.EXPIRED,
19238
+ DEVICE_AUTH_STATUS.UNKNOWN,
19239
+ DEVICE_AUTH_STATUS.INVALID_DECISION,
19240
+ DEVICE_AUTH_STATUS.IDENTITY_REQUIRED,
19241
+ DEVICE_AUTH_STATUS.NOT_CONFIGURED,
19242
+ DEVICE_AUTH_STATUS.UNAUTHORIZED,
19243
+ DEVICE_AUTH_STATUS.ALREADY_DECIDED,
19244
+ DEVICE_AUTH_STATUS.TERMS_REQUIRED
19245
+ ]),
19246
+ message: external_exports.string().min(1)
19247
+ }).strict();
19248
+ var devicePollResponseSchema = external_exports.union([
19249
+ devicePollPendingSchema,
19250
+ devicePollApprovedSchema,
19251
+ deviceAuthRefusalSchema
19252
+ ]);
19253
+ var deviceConfirmRequestSchema = external_exports.object({
19254
+ userCode: external_exports.string().min(1),
19255
+ decision: external_exports.enum(["approve", "decline"]),
19256
+ externalAuthId: external_exports.string().min(1).optional(),
19257
+ email: external_exports.string().optional(),
19258
+ acceptTerms: external_exports.boolean().optional()
19259
+ }).strict();
19260
+ var deviceConfirmRecordedSchema = external_exports.object({
19261
+ status: external_exports.literal(DEVICE_AUTH_STATUS.RECORDED),
19262
+ accountId: external_exports.string().min(1).optional()
19263
+ }).strict();
19264
+ var deviceConfirmResponseSchema = external_exports.union([
19265
+ deviceConfirmRecordedSchema,
19266
+ deviceAuthRefusalSchema
19106
19267
  ]);
19268
+ var DASHBOARD_URL = "https://app.halfcycle.ai";
19107
19269
 
19108
- // ../events/dist/pacing.js
19109
- var CAREFUL_START_AGENTS = 3;
19110
- var KEEP_FREE_MAX = 90;
19111
- var keepFreePercentSchema = external_exports.number().int().min(0).max(KEEP_FREE_MAX);
19112
- var KEEP_FREE_DEFAULT = 20;
19113
- var PACE_AGENTS_MAX = 20;
19114
- var FIVE_HOUR_WINDOW_MINUTES = 300;
19115
- var SEVEN_DAY_WINDOW_MINUTES = 10080;
19116
- var PACE_WINDOW_MINUTES_MAX = 525600;
19117
- var MINUTE_KEY = /^([1-9][0-9]{0,6})-minute$/;
19118
- function isPaceWindowKind(value) {
19119
- if (value === "five-hour" || value === "seven-day")
19120
- return true;
19121
- if (typeof value !== "string")
19122
- return false;
19123
- const match = MINUTE_KEY.exec(value);
19124
- if (match === null)
19125
- return false;
19126
- const minutes = Number(match[1]);
19127
- return minutes <= PACE_WINDOW_MINUTES_MAX && minutes !== FIVE_HOUR_WINDOW_MINUTES && minutes !== SEVEN_DAY_WINDOW_MINUTES;
19270
+ // ../events/dist/project-page.js
19271
+ function projectPageUrl(engagementId) {
19272
+ return isEngagementId(engagementId) ? `${DASHBOARD_URL}/${encodeURIComponent(engagementId)}` : void 0;
19128
19273
  }
19129
- var paceWindowKindSchema = external_exports.custom((value) => isPaceWindowKind(value), {
19130
- message: "not a usage window key"
19274
+ function systemViewUrl(engagementId, phase) {
19275
+ const page = projectPageUrl(engagementId);
19276
+ if (page === void 0)
19277
+ return void 0;
19278
+ return `${page}/console/system-view?${new URLSearchParams({ phase }).toString()}`;
19279
+ }
19280
+ function requestAccessSentence(engagementId) {
19281
+ const page = projectPageUrl(engagementId) ?? "this project's page on Halfcycle";
19282
+ return `To ask for access, open ${page} signed in as this account and choose "Request access". Then let the project's owner know, so they can approve it.`;
19283
+ }
19284
+
19285
+ // ../events/dist/scope-notices.js
19286
+ var scopePartNameSchema = external_exports.string().min(1).max(SYSTEM_PART_NAME_MAX);
19287
+ var scopeNoteEntrySchema = external_exports.object({
19288
+ part: scopePartNameSchema.optional(),
19289
+ note: external_exports.string().min(1).max(SCOPE_NOTE_MAX)
19290
+ }).strict();
19291
+ var scopeSentBackSchema = external_exports.object({
19292
+ phase: scopePhaseSchema,
19293
+ number: external_exports.number().int().min(1),
19294
+ notes: external_exports.array(scopeNoteEntrySchema).min(1).max(PHASE_PLAN_CHANGES_MAX + 1)
19295
+ }).strict().superRefine((entry, ctx) => {
19296
+ entry.notes.forEach((n, i) => {
19297
+ if (n.part === void 0 && i !== entry.notes.length - 1) {
19298
+ ctx.addIssue({
19299
+ code: external_exports.ZodIssueCode.custom,
19300
+ path: ["notes", i],
19301
+ message: "the note on the whole phase comes after every note on a part"
19302
+ });
19303
+ }
19304
+ });
19131
19305
  });
19132
- function paceWindowMinutesOf(kind) {
19133
- if (kind === "five-hour")
19134
- return FIVE_HOUR_WINDOW_MINUTES;
19135
- if (kind === "seven-day")
19136
- return SEVEN_DAY_WINDOW_MINUTES;
19137
- return Number(MINUTE_KEY.exec(kind)?.[1] ?? 0);
19306
+ var scopeWaitingSchema = external_exports.object({
19307
+ phase: scopePhaseSchema,
19308
+ number: external_exports.number().int().min(1),
19309
+ changedParts: external_exports.array(scopePartNameSchema).max(PHASE_PLAN_CHANGES_MAX)
19310
+ }).strict();
19311
+ var scopeNoticesSchema = external_exports.object({
19312
+ sentBack: external_exports.array(scopeSentBackSchema),
19313
+ waiting: external_exports.array(scopeWaitingSchema),
19314
+ typedApprovalOffered: external_exports.boolean()
19315
+ }).strict().refine((v) => v.sentBack.length > 0 || v.waiting.length > 0, {
19316
+ message: "carried only when a phase was sent back or waits for the owner"
19317
+ });
19318
+ var scopeApprovedNoticeSchema = external_exports.object({ phase: scopePhaseSchema }).strict();
19319
+ var scopeApprovedNoticesSchema = external_exports.array(scopeApprovedNoticeSchema);
19320
+ function approvedInConsoleSentence(phase) {
19321
+ return `Scope for ${phaseWords(phase)} was approved in the console; continuing. Say so to the person, then carry on with the next step; they do not need to tell you.`;
19138
19322
  }
19139
- function paceWindowKindOf(minutes) {
19140
- if (!Number.isInteger(minutes) || minutes < 1 || minutes > PACE_WINDOW_MINUTES_MAX)
19141
- return null;
19142
- if (minutes === FIVE_HOUR_WINDOW_MINUTES)
19143
- return "five-hour";
19144
- if (minutes === SEVEN_DAY_WINDOW_MINUTES)
19145
- return "seven-day";
19146
- return `${String(minutes)}-minute`;
19323
+ function quotedParts(parts) {
19324
+ return parts.map((p) => `"${p}"`).join(", ");
19147
19325
  }
19148
- function paceWindowWordsOf(kind) {
19149
- const minutes = paceWindowMinutesOf(kind);
19150
- if (minutes === FIVE_HOUR_WINDOW_MINUTES)
19151
- return "5-hour window";
19152
- if (minutes === SEVEN_DAY_WINDOW_MINUTES)
19153
- return "weekly window";
19154
- if (minutes % 1440 === 0)
19155
- return `${String(minutes / 1440)}-day window`;
19156
- if (minutes % 60 === 0)
19157
- return `${String(minutes / 60)}-hour window`;
19158
- return `${String(minutes)}-minute window`;
19326
+ function approvalSentence(phase, offerTyping) {
19327
+ if (offerTyping === "off")
19328
+ return TYPED_APPROVAL_OFF_SENTENCE;
19329
+ if (offerTyping === "update")
19330
+ return CLIENT_UPDATE_SENTENCE;
19331
+ return `In their own interactive session ${HEADLESS_CANNOT_APPROVE}, they ask to see the plan, check each part, and then type: approve ${phase}.`;
19159
19332
  }
19160
- function paceShareWordsOf(keepFreePercent) {
19161
- return keepFreePercent === 20 ? "a fifth" : `${String(keepFreePercent)}%`;
19333
+ function phaseWords(phase) {
19334
+ return `phase "${phase}"`;
19162
19335
  }
19163
- var PACE_STATES = ["full", "slowing", "holding"];
19164
- var paceStateSchema = external_exports.enum(PACE_STATES);
19165
- var PACE_SHARE_GIVEN_IN = ["chat", "agent", "relayed"];
19166
- var paceShareGivenInSchema = external_exports.enum(PACE_SHARE_GIVEN_IN);
19167
- var paceShareRequestSchema = external_exports.object({ keepFreePercent: keepFreePercentSchema, givenIn: paceShareGivenInSchema }).strict();
19168
- var paceShareResultSchema = external_exports.discriminatedUnion("outcome", [
19169
- external_exports.object({
19170
- outcome: external_exports.literal("recorded"),
19171
- keepFreePercent: keepFreePercentSchema,
19172
- moreAllowance: external_exports.boolean().optional()
19173
- }).strict(),
19174
- external_exports.object({ outcome: external_exports.literal("needs-the-person"), keepFreePercent: keepFreePercentSchema }).strict(),
19175
- external_exports.object({ outcome: external_exports.literal("not-owner") }).strict(),
19176
- external_exports.object({ outcome: external_exports.literal("no-owner") }).strict(),
19177
- external_exports.object({ outcome: external_exports.literal("unavailable") }).strict()
19178
- ]);
19179
- var pacingBasisTaskSchema = external_exports.object({
19180
- id: buildTaskIdSchema,
19181
- dependsOn: external_exports.array(buildTaskIdSchema).max(BUILD_SNAPSHOT_TASK_LIMIT),
19182
- budgetMinutes: external_exports.number().int().min(1).max(BUILD_TASK_BUDGET_MAX_MINUTES).optional(),
19183
- human: external_exports.boolean(),
19184
- kind: buildTaskKindSchema.optional()
19185
- }).strict();
19186
- var pacingBasisSchema = external_exports.object({
19187
- outcome: external_exports.literal("basis"),
19188
- claudePlan: external_exports.string().nullable().optional(),
19189
- keepFreePercent: keepFreePercentSchema,
19190
- settingsUrl: external_exports.string().optional(),
19191
- phase: phaseNameSchema.nullable(),
19192
- tasks: external_exports.array(pacingBasisTaskSchema).max(BUILD_SNAPSHOT_TASK_LIMIT),
19193
- medianTaskMinutes: external_exports.number().nonnegative().nullable(),
19194
- medianTaskTokens: external_exports.number().nonnegative().nullable()
19195
- }).strict();
19196
- var paceSuggestionSchema = external_exports.object({
19197
- number: external_exports.number().int().min(1).max(PACE_AGENTS_MAX),
19198
- minutes: external_exports.number().int().nonnegative().nullable(),
19199
- pauses: external_exports.number().int().nonnegative(),
19200
- keepFreePercent: keepFreePercentSchema,
19201
- knownWindow: paceWindowKindSchema.nullable(),
19202
- expectedTokens: external_exports.number().int().nonnegative().optional(),
19203
- limitTokens: external_exports.number().int().positive().optional(),
19204
- multiple: external_exports.number().positive().nullable().optional(),
19205
- firstGuess: external_exports.boolean().optional(),
19206
- limitSetBy: external_exports.enum(["default", "person", "other"]).optional()
19207
- }).strict();
19208
- var PACE_NOT_PACED_REASONS = ["not-configured", "unreadable"];
19209
- var limitHeldSchema = external_exports.object({
19210
- used: external_exports.number().int().nonnegative(),
19211
- limit: external_exports.number().int().positive(),
19212
- raiseBy: external_exports.number().int().nonnegative(),
19213
- working: external_exports.number().int().nonnegative()
19214
- }).strict();
19215
- var paceUsageSchema = external_exports.object({
19216
- used: external_exports.number().int().nonnegative(),
19217
- limit: external_exports.number().int().positive()
19218
- }).strict();
19219
- var paceRunResultSchema = external_exports.discriminatedUnion("outcome", [
19220
- external_exports.object({
19221
- outcome: external_exports.literal("paced"),
19222
- pace: paceStateSchema,
19223
- number: external_exports.number().int().min(1).max(PACE_AGENTS_MAX),
19224
- allowedNow: external_exports.number().int().nonnegative(),
19225
- windowKind: paceWindowKindSchema.optional(),
19226
- until: utcIso8601.optional(),
19227
- keepFreePercent: keepFreePercentSchema.optional(),
19228
- suggestion: paceSuggestionSchema.optional(),
19229
- limit: external_exports.literal("reached").optional(),
19230
- limitHeld: limitHeldSchema.optional(),
19231
- usage: paceUsageSchema.optional()
19232
- }).strict(),
19233
- external_exports.object({ outcome: external_exports.literal("not-paced"), reason: external_exports.enum(PACE_NOT_PACED_REASONS) }).strict()
19234
- ]);
19235
- function tokenCountWordsOf(n) {
19236
- if (n < 1e3)
19237
- return String(Math.max(0, Math.round(n)));
19238
- const rounded = Number(n.toPrecision(2));
19239
- const [unit, word] = rounded >= 1e9 ? [1e9, "billion"] : rounded >= 1e6 ? [1e6, "million"] : [1e3, "thousand"];
19240
- return `${String(Number((rounded / unit).toPrecision(2)))} ${word}`;
19336
+ function sentBackNotice(entry) {
19337
+ return [
19338
+ `Before anything else: the project's owner sent the scope of ${phaseWords(entry.phase)} back with these notes.`,
19339
+ ...entry.notes.map((n) => n.part === void 0 ? `- ${n.note}` : `- "${n.part}": ${n.note}`),
19340
+ `Change the plan to answer every note, then send it again: say "plan the next phase" to start. It then waits for the owner's approval again.`
19341
+ ].join("\n");
19241
19342
  }
19242
- function paceAgentsOf(n) {
19243
- return n === 1 ? "1 agent" : `${String(n)} agents`;
19343
+ function systemViewSentence(offerTyping, address) {
19344
+ if (address === void 0)
19345
+ return [];
19346
+ return [offerTyping === "yes" ? `Or they approve it in the System view: ${address}` : `They approve it in the System view: ${address}`];
19244
19347
  }
19245
- var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
19246
- var MONTH_NAMES = [
19247
- "January",
19248
- "February",
19249
- "March",
19250
- "April",
19251
- "May",
19252
- "June",
19253
- "July",
19254
- "August",
19255
- "September",
19256
- "October",
19257
- "November",
19258
- "December"
19259
- ];
19260
- var SIX_DAYS_MS = 6 * 24 * 60 * 60 * 1e3;
19261
- function paceClockOf(at, now) {
19262
- const hours = at.getHours();
19263
- const clock = `${String(hours % 12 === 0 ? 12 : hours % 12)}:${String(at.getMinutes()).padStart(2, "0")} ${hours < 12 ? "am" : "pm"}`;
19264
- const sameDay = at.getFullYear() === now.getFullYear() && at.getMonth() === now.getMonth() && at.getDate() === now.getDate();
19265
- if (sameDay)
19266
- return clock;
19267
- const day = DAY_NAMES[at.getDay()] ?? "";
19268
- if (at.getTime() - now.getTime() > SIX_DAYS_MS) {
19269
- return `${day} ${String(at.getDate())} ${MONTH_NAMES[at.getMonth()] ?? ""} ${clock}`;
19270
- }
19271
- return `${day} ${clock}`;
19348
+ function waitingNotice(entry, offerTyping, engagementId) {
19349
+ return [
19350
+ ...entry.changedParts.length > 0 ? [`These parts changed since the owner last decided on this phase, and wait for their approval: ${quotedParts(entry.changedParts)}.`] : [],
19351
+ `The scope of ${phaseWords(entry.phase)} is waiting for the project's owner to approve it.`,
19352
+ approvalSentence(entry.phase, offerTyping),
19353
+ ...systemViewSentence(offerTyping, systemViewUrl(engagementId, entry.phase))
19354
+ ].join(" ");
19272
19355
  }
19273
- var PACE_P5 = "This build may use all of your allowance. It will not slow down to leave room for you.";
19274
- function paceS1Of(window, k, until, now, keepFreePercent) {
19275
- return `Halfcycle is slowing this build so you keep about ${paceShareWordsOf(keepFreePercent)} of this ${paceWindowWordsOf(window)} for your own work. It will run at most ${paceAgentsOf(k)} at once until ${paceClockOf(until, now)}.`;
19356
+ function scopeNoticeText(scope, options) {
19357
+ const omit2 = options.omitActivePhase === true ? options.activePhase : void 0;
19358
+ return {
19359
+ sentBack: scope.sentBack.map(sentBackNotice),
19360
+ waiting: scope.waiting.filter((w) => omit2 === void 0 || w.phase !== omit2).map((w) => waitingNotice(w, options.offerTyping, options.engagementId))
19361
+ };
19362
+ }
19363
+
19364
+ // ../events/dist/review-notices.js
19365
+ var reviewNoticeQuestionSchema = external_exports.object({
19366
+ phase: scopePhaseSchema,
19367
+ number: reviewPhaseNumberSchema,
19368
+ part: systemPartIdSchema,
19369
+ partName: systemPartSchema.shape.name,
19370
+ question: reviewNoteSchema
19371
+ }).strict();
19372
+ var reviewNoticeSentBackSchema = external_exports.object({
19373
+ phase: scopePhaseSchema,
19374
+ number: reviewPhaseNumberSchema,
19375
+ part: systemPartIdSchema,
19376
+ partName: systemPartSchema.shape.name,
19377
+ note: reviewNoteSchema
19378
+ }).strict();
19379
+ var reviewNoticesSchema = external_exports.object({
19380
+ questions: external_exports.array(reviewNoticeQuestionSchema).max(REVIEW_PARTS_MAX),
19381
+ sentBack: external_exports.array(reviewNoticeSentBackSchema).max(REVIEW_PARTS_MAX)
19382
+ }).strict().refine((v) => v.questions.length > 0 || v.sentBack.length > 0, {
19383
+ message: "carried only when a question waits or a part was sent back"
19384
+ });
19385
+ function phaseCalled(phase) {
19386
+ return `phase "${phase}"`;
19276
19387
  }
19277
- function paceS2Of(window, k, until, now, keepFreePercent) {
19278
- const head = `Halfcycle has paused new work in this build until ${paceClockOf(until, now)}, when your ${paceWindowWordsOf(window)} resets, so you keep about ${paceShareWordsOf(keepFreePercent)} of it for your own work.`;
19279
- if (k === 0)
19280
- return head;
19281
- return k === 1 ? `${head} The 1 agent already working will finish its task.` : `${head} The ${String(k)} agents already working will finish their tasks.`;
19388
+ function questionNotice(entry) {
19389
+ const answer = `${HALFCYCLE_COMMAND} review ${quoteForShell(entry.phase)} --answer ${quoteForShell(entry.part)} "<your answer>"`;
19390
+ return `Before anything else: the project's owner asked about "${entry.partName}" in the review of ${phaseCalled(entry.phase)}: ${entry.question} Answer it with ${answer}.`;
19282
19391
  }
19283
- function paceS3Of(window, n) {
19284
- return `Your ${paceWindowWordsOf(window)} has reset. Halfcycle is starting new work again, up to ${paceAgentsOf(n)} at once.`;
19392
+ function sentBackNotice2(entry) {
19393
+ return `Before anything else: the project's owner sent "${entry.partName}" in ${phaseCalled(entry.phase)} back to building: ${entry.note} Rebuild it: add or reopen the tasks that build it. For each task that rebuilds it, add a task that reviews the rebuild, held by a different agent from the one that built it. When each review has passed, report the build with the time each task finished, then run the close step again.`;
19285
19394
  }
19286
- function paceOfferTextOf(input) {
19287
- const canFaster = input.number < PACE_AGENTS_MAX;
19288
- const canUseMore = input.keepFreePercent > 0;
19289
- if (!canFaster && !canUseMore)
19290
- return null;
19291
- const head = `You have used only ${String(Math.floor(input.percentUsed))}% of this ${paceWindowWordsOf(input.window)}, and more than half of it has passed.`;
19292
- const useUpTo = `"use up to ${String(Math.min(100, 100 - input.keepFreePercent + 10))}%" to let this build use more of it`;
19293
- if (canFaster && canUseMore)
19294
- return `${head} Say "faster" for one more agent, or ${useUpTo}.`;
19295
- if (canFaster)
19296
- return `${head} Say "faster" for one more agent.`;
19297
- return `${head} Say ${useUpTo}.`;
19395
+ function reviewNoticeText(review) {
19396
+ return [...review.questions.map(questionNotice), ...review.sentBack.map(sentBackNotice2)];
19298
19397
  }
19299
- function paceF1Of(k) {
19300
- return `This build runs at most ${paceAgentsOf(k)} at once.`;
19398
+
19399
+ // ../events/dist/credential-wire.js
19400
+ var engagementCredentialResponseSchema = external_exports.object({
19401
+ engagementId: external_exports.string(),
19402
+ sessionToken: external_exports.string(),
19403
+ mcpUrl: external_exports.string(),
19404
+ guardUrl: external_exports.string(),
19405
+ controlTelemetryUrl: external_exports.string(),
19406
+ methodVersion: external_exports.string().optional(),
19407
+ consoleUrl: external_exports.string().optional()
19408
+ }).strict();
19409
+ var ciTokenExchangeResponseSchema = external_exports.object({
19410
+ token: external_exports.string(),
19411
+ expiresAt: utcIso8601,
19412
+ engagementId: external_exports.string()
19413
+ }).strict();
19414
+ var NO_CREDENTIAL_PLACEHOLDER = "halfcycle:no-credential";
19415
+ var NOT_THIS_ACCOUNT_PLACEHOLDER = "halfcycle:not-this-account";
19416
+ var UPDATE_CLAUDE_CODE_PLACEHOLDER = "halfcycle:update-claude-code";
19417
+ var CREDENTIAL_PLACEHOLDERS = [
19418
+ NO_CREDENTIAL_PLACEHOLDER,
19419
+ NOT_THIS_ACCOUNT_PLACEHOLDER,
19420
+ UPDATE_CLAUDE_CODE_PLACEHOLDER
19421
+ ];
19422
+ var SWITCH_ACCOUNT_ANSWER = 'answer "s" when asked';
19423
+ var NO_CREDENTIAL_SENTENCE = 'Halfcycle is not connected: this machine has no working Halfcycle credential for this project. Run "npx halfcycle" in the project folder to sign in again.';
19424
+ var NOT_THIS_ACCOUNT_SENTENCE = `Halfcycle is not connected: this project belongs to another Halfcycle account. To switch, run "npx halfcycle" in this project and ${SWITCH_ACCOUNT_ANSWER}.`;
19425
+ var UPDATE_CLAUDE_CODE_SENTENCE = "Halfcycle is not connected: this Claude Code version does not name the server it connects to, so no credential was sent. Update Claude Code, then reopen.";
19426
+ var CANNOT_REACH_SENTENCE = "Halfcycle is not connected: its server could not be reached, so its tools are unavailable in this session. Check your network, then reopen the project.";
19427
+ var SERVICE_PROBLEM_SENTENCE = "Halfcycle is not connected: its service had a problem just now, so nothing in this session is being checked. Reopen the project to try again.";
19428
+ var FOLDER_NOT_TRUSTED_SENTENCE = "Folder not yet trusted. Open this folder once in interactive Claude Code and trust it; then headless runs work.";
19429
+ var SCOPE_UNCHECKED_SENTENCE = "If this project needs approval before building, that cannot be checked either, so work done now shows as built without approval unless already approved.";
19430
+ var PLACEHOLDER_SENTENCES = Object.freeze({
19431
+ [NO_CREDENTIAL_PLACEHOLDER]: NO_CREDENTIAL_SENTENCE,
19432
+ [NOT_THIS_ACCOUNT_PLACEHOLDER]: NOT_THIS_ACCOUNT_SENTENCE,
19433
+ [UPDATE_CLAUDE_CODE_PLACEHOLDER]: UPDATE_CLAUDE_CODE_SENTENCE
19434
+ });
19435
+
19436
+ // ../events/dist/scope-chat.js
19437
+ var chatScopeApproveRequestSchema = external_exports.object({
19438
+ act: external_exports.literal("approve"),
19439
+ reference: phaseIdentitySchema,
19440
+ planVersion: planVersionSchema
19441
+ }).strict();
19442
+ var chatScopeSendBackRequestSchema = external_exports.object({
19443
+ act: external_exports.literal("send-back"),
19444
+ reference: phaseIdentitySchema,
19445
+ planVersion: planVersionSchema,
19446
+ note: scopeNoteWireSchema.optional()
19447
+ }).strict();
19448
+ var chatScopeRequestSchema = external_exports.discriminatedUnion("act", [
19449
+ chatScopeApproveRequestSchema,
19450
+ chatScopeSendBackRequestSchema
19451
+ ]);
19452
+ var CHAT_SCOPE_ACTS = SCOPE_ACTS;
19453
+ var chatScopeRecordedSchema = external_exports.object({
19454
+ outcome: external_exports.literal("recorded"),
19455
+ act: external_exports.enum(CHAT_SCOPE_ACTS),
19456
+ phase: scopePhaseSchema,
19457
+ planVersion: planVersionSchema
19458
+ }).strict();
19459
+ var chatScopeAlreadyApprovedSchema = external_exports.object({
19460
+ outcome: external_exports.literal("already-approved"),
19461
+ replanNeeded: external_exports.boolean()
19462
+ }).strict();
19463
+ var chatScopeRefusedSchema = external_exports.object({
19464
+ outcome: external_exports.literal("refused"),
19465
+ problems: external_exports.array(systemDescriptionProblemSchema).min(1)
19466
+ }).strict();
19467
+ var chatAmbiguousSchema = external_exports.object({
19468
+ outcome: external_exports.literal("ambiguous"),
19469
+ phases: external_exports.array(scopePhaseSchema).min(2)
19470
+ }).strict();
19471
+ function bareOutcome(outcome) {
19472
+ return external_exports.object({ outcome: external_exports.literal(outcome) }).strict();
19301
19473
  }
19302
- function paceAgentLineOf(pace, allowance, until, now) {
19303
- const fewer = `once fewer than ${paceAgentsOf(allowance)} are working.`;
19304
- if (until === null)
19305
- return `Start it ${fewer}`;
19306
- if (pace === "holding")
19307
- return `Start it after ${paceClockOf(until, now)}.`;
19308
- return `Start it after ${paceClockOf(until, now)}, or ${fewer}`;
19474
+ var chatScopeResultSchema = external_exports.discriminatedUnion("outcome", [
19475
+ chatScopeRecordedSchema,
19476
+ chatScopeAlreadyApprovedSchema,
19477
+ chatScopeRefusedSchema,
19478
+ chatAmbiguousSchema,
19479
+ bareOutcome("show-first"),
19480
+ bareOutcome("plan-changed"),
19481
+ bareOutcome("build-started"),
19482
+ bareOutcome("not-owner"),
19483
+ bareOutcome("no-owner"),
19484
+ bareOutcome("not-offered"),
19485
+ bareOutcome("unavailable")
19486
+ ]);
19487
+
19488
+ // ../events/dist/review-chat.js
19489
+ var chatReviewAcceptRequestSchema = external_exports.object({
19490
+ act: external_exports.literal("accept"),
19491
+ reference: phaseIdentitySchema,
19492
+ reviewVersion: external_exports.number().int().min(0),
19493
+ parts: external_exports.array(systemPartIdSchema).max(REVIEW_PARTS_MAX),
19494
+ waitingClaims: external_exports.literal(true).optional()
19495
+ }).strict();
19496
+ function notedRequest(act) {
19497
+ return external_exports.object({
19498
+ act: external_exports.literal(act),
19499
+ reference: phaseIdentitySchema,
19500
+ reviewVersion: external_exports.number().int().min(0),
19501
+ part: systemPartIdSchema,
19502
+ note: reviewNoteWireSchema
19503
+ }).strict();
19309
19504
  }
19310
- var PACE_NOTHING_CHANGED = "Nothing was changed.";
19311
- function paceHeldStartTextOf(input) {
19312
- const { pace, allowance, working, window, until, now, keepFreePercent } = input;
19313
- if (pace === "full" || window === null || until === null) {
19314
- return `${paceF1Of(allowance)} ${paceAgentLineOf("full", allowance, null, now)} ${PACE_NOTHING_CHANGED}`;
19315
- }
19316
- const head = pace === "slowing" ? paceS1Of(window, allowance, until, now, keepFreePercent) : paceS2Of(window, working, until, now, keepFreePercent);
19317
- return `${head} ${paceAgentLineOf(pace, allowance, until, now)} ${PACE_NOTHING_CHANGED}`;
19505
+ var chatReviewQuestionRequestSchema = notedRequest("question");
19506
+ var chatReviewSendBackRequestSchema = notedRequest("send-back");
19507
+ var chatReviewCarryRequestSchema = notedRequest("carry");
19508
+ var chatReviewDropRequestSchema = notedRequest("drop");
19509
+ var chatReviewRejectRequestSchema = external_exports.object({
19510
+ act: external_exports.literal("reject"),
19511
+ reference: phaseIdentitySchema,
19512
+ reviewVersion: external_exports.number().int().min(0),
19513
+ task: buildTaskIdSchema,
19514
+ note: reviewNoteWireSchema
19515
+ }).strict();
19516
+ var chatReviewRequestSchema = external_exports.discriminatedUnion("act", [
19517
+ chatReviewAcceptRequestSchema,
19518
+ chatReviewQuestionRequestSchema,
19519
+ chatReviewSendBackRequestSchema,
19520
+ chatReviewCarryRequestSchema,
19521
+ chatReviewDropRequestSchema,
19522
+ chatReviewRejectRequestSchema
19523
+ ]);
19524
+ function bareOutcome2(outcome) {
19525
+ return external_exports.object({ outcome: external_exports.literal(outcome) }).strict();
19318
19526
  }
19319
- var PACE_FIRST_READ = "Reading this computer's usage records for the first time. This can take a minute.";
19320
- var PACE_AGENTS_TOO_MANY = `Run at most ${String(PACE_AGENTS_MAX)} agents at once.`;
19321
- var PACE_ONLY_PERSON_RAISES = 'Only the person can raise the number of agents, by typing "faster" or "run 5 agents" in the chat. Nothing was changed.';
19322
- function paceStatusLineOf(percent, window) {
19323
- return `Halfcycle \xB7 ${String(Math.round(percent))}% of this ${paceWindowWordsOf(window)} used`;
19527
+ function notTheConsoleNotDecidable(option) {
19528
+ return option !== reviewNotDecidableSchema;
19324
19529
  }
19325
- var STARTING_TOKENS_PER_TASK = 9e6;
19326
- var LIMIT_MULTIPLE_DEFAULT = 2;
19327
- var LIMIT_MULTIPLE_RANGE = { min: 1, max: 20 };
19328
- var LIMIT_FIGURE_RANGE = { min: 1e6, max: 1e11 };
19329
- var LIMIT_HISTORY_MAX = 20;
19330
- var LIMIT_ROUND_TOKENS = 1e3;
19331
- var LIMIT_COUNTED_KINDS = ["build", "review", "check"];
19332
- var LIMIT_PARTS = ["half", "three-quarters", "nine-tenths", "limit"];
19333
- var limitPartSchema = external_exports.enum(LIMIT_PARTS);
19334
- var LIMIT_PART_SHARE = {
19335
- half: 0.5,
19336
- "three-quarters": 0.75,
19337
- "nine-tenths": 0.9,
19338
- limit: 1
19339
- };
19340
- var LIMIT_SET_BY = ["default", "person", "other"];
19341
- var limitSetBySchema = external_exports.enum(LIMIT_SET_BY);
19342
- var LIMIT_TYPED_BY = ["person", "not-attended", "cannot-tell"];
19343
- var limitTypedBySchema = external_exports.enum(LIMIT_TYPED_BY);
19344
- var limitHistoryEntrySchema = external_exports.object({
19345
- at: utcIso8601,
19346
- kind: external_exports.enum(["raised", "set", "stopped"]),
19347
- limit: external_exports.number().int().positive(),
19348
- by: limitTypedBySchema,
19349
- relayed: external_exports.literal(true).optional()
19530
+ var chatReviewResultSchema = external_exports.discriminatedUnion("outcome", [
19531
+ reviewPartsNotDecidableSchema,
19532
+ ...reviewDecisionResultSchema.options.filter(notTheConsoleNotDecidable),
19533
+ bareOutcome2("show-first"),
19534
+ bareOutcome2("not-offered"),
19535
+ chatAmbiguousSchema,
19536
+ bareOutcome2("nothing-waiting")
19537
+ ]);
19538
+ var reviewAnswerRequestSchema = external_exports.object({
19539
+ part: systemPartIdSchema,
19540
+ answer: statementText(REVIEW_ANSWER_MAX)
19350
19541
  }).strict();
19351
- var PHASE_LIMIT_FILENAME = "limit.json";
19352
- var phaseLimitFileSchema = external_exports.object({
19353
- phase: phaseNameSchema,
19354
- expected: external_exports.number().int().nonnegative(),
19355
- expectedMinutes: external_exports.number().nonnegative().nullable(),
19356
- limit: external_exports.number().int().positive(),
19357
- multiple: external_exports.number().positive().nullable(),
19358
- setBy: limitSetBySchema,
19359
- firstGuess: external_exports.boolean(),
19360
- shown: external_exports.array(limitPartSchema).max(LIMIT_PARTS.length),
19361
- awaiting: external_exports.boolean(),
19362
- history: external_exports.array(limitHistoryEntrySchema).max(LIMIT_HISTORY_MAX)
19542
+ var reviewAnswerRecordedSchema = external_exports.object({
19543
+ outcome: external_exports.literal("recorded"),
19544
+ name: systemPartSchema.shape.name
19363
19545
  }).strict();
19364
- function phaseLimitFileOf(raw, phase) {
19365
- const parsed = phaseLimitFileSchema.safeParse(raw);
19366
- if (!parsed.success)
19367
- return null;
19368
- return phase === null || parsed.data.phase === phase ? parsed.data : null;
19546
+ var reviewAnswerResultSchema = external_exports.discriminatedUnion("outcome", [
19547
+ reviewAnswerRecordedSchema,
19548
+ bareOutcome2("no-question"),
19549
+ reviewRefusedSchema,
19550
+ bareOutcome2("unavailable")
19551
+ ]);
19552
+
19553
+ // ../events/dist/owner-wait-answer.js
19554
+ var OWNER_WAIT_SHORT_ID_LENGTH = 8;
19555
+ var ownerWaitShortIdSchema = external_exports.string().regex(/^[0-9a-f]{8}$/);
19556
+ function ownerWaitShortIdOf(declarationId) {
19557
+ return declarationId.slice(0, OWNER_WAIT_SHORT_ID_LENGTH).toLowerCase();
19369
19558
  }
19370
- var phaseUsageFileSchema = external_exports.object({
19371
- phase: phaseNameSchema,
19372
- openedAt: utcIso8601,
19373
- tokens: external_exports.number().int().nonnegative(),
19374
- at: utcIso8601
19375
- }).strict();
19376
- function limitPartLineOf(part, used, limit) {
19377
- const words = part === "half" ? "half" : part === "three-quarters" ? "three quarters" : "nine tenths";
19378
- const line2 = `This phase has used ${words} of its usage limit: ${tokenCountWordsOf(used)} of ${tokenCountWordsOf(limit)} tokens.`;
19379
- return part === "nine-tenths" ? `${line2} New work stops at the limit.` : line2;
19559
+ var OWNER_WAIT_ANSWER_ACTS = ["approve", "decline"];
19560
+ var ownerWaitAnswerActSchema = external_exports.enum(OWNER_WAIT_ANSWER_ACTS);
19561
+ function ownerWaitApproveLineOf(declarationId) {
19562
+ return `approve wait ${ownerWaitShortIdOf(declarationId)}`;
19380
19563
  }
19381
- function limitReachedLineOf(used, limit, working, raiseBy) {
19382
- const head = `This phase has reached its usage limit: ${tokenCountWordsOf(used)} of ${tokenCountWordsOf(limit)} tokens. No new work will start.`;
19383
- const finish = working === 0 ? "" : working === 1 ? " The 1 agent already working will finish its task." : ` The ${String(working)} agents already working will finish their tasks.`;
19384
- return `${head}${finish} Say "keep going" to raise the limit by about ${tokenCountWordsOf(raiseBy)} tokens and carry on, or "stop" to leave the phase paused here. Nothing is lost either way.`;
19564
+ function ownerWaitDeclineLineOf(declarationId) {
19565
+ return `decline wait ${ownerWaitShortIdOf(declarationId)}: <why>`;
19385
19566
  }
19386
- function limitHeldStartTextOf(limit) {
19387
- return `This phase has reached its usage limit of ${tokenCountWordsOf(limit)} tokens, so no new agent starts. Ask the person to say "keep going" or "stop", and start nothing until they have. ${PACE_NOTHING_CHANGED}`;
19567
+ var ownerWaitChatRequestSchema = external_exports.discriminatedUnion("act", [
19568
+ external_exports.object({ act: external_exports.literal("approve"), wait: ownerWaitShortIdSchema }).strict(),
19569
+ external_exports.object({ act: external_exports.literal("decline"), wait: ownerWaitShortIdSchema, note: scopeNoteWireSchema }).strict()
19570
+ ]);
19571
+ function bareOutcome3(outcome) {
19572
+ return external_exports.object({ outcome: external_exports.literal(outcome) }).strict();
19388
19573
  }
19574
+ var ownerWaitChatResultSchema = external_exports.discriminatedUnion("outcome", [
19575
+ external_exports.object({
19576
+ outcome: external_exports.literal("recorded"),
19577
+ act: ownerWaitAnswerActSchema,
19578
+ question: external_exports.string().min(1)
19579
+ }).strict(),
19580
+ external_exports.object({ outcome: external_exports.literal("refused"), problems: external_exports.array(systemDescriptionProblemSchema).min(1) }).strict(),
19581
+ bareOutcome3("not-owner"),
19582
+ bareOutcome3("no-owner"),
19583
+ bareOutcome3("not-found"),
19584
+ bareOutcome3("ambiguous"),
19585
+ bareOutcome3("not-offered"),
19586
+ bareOutcome3("unavailable")
19587
+ ]);
19588
+
19589
+ // ../events/dist/owner-waits.js
19590
+ var OWNER_WAIT_ITEMS = Object.freeze([
19591
+ Object.freeze({ id: "delete-users-data", words: "Deleting or replacing data people already have." }),
19592
+ Object.freeze({
19593
+ id: "change-promises",
19594
+ words: "Changing what the product promises its users, such as pricing, terms or privacy."
19595
+ }),
19596
+ Object.freeze({ id: "spend-or-sign-up", words: "Anything that costs money or signs up for a service." }),
19597
+ Object.freeze({ id: "publish-or-release", words: "Publishing or releasing to users." }),
19598
+ Object.freeze({ id: "reshape-shared-data", words: "Dropping or reshaping stored data that other parts read." }),
19599
+ Object.freeze({ id: "change-standing-rule", words: "Changing a standing rule." })
19600
+ ]);
19601
+ var OWNER_WAIT_ITEM_IDS = Object.freeze(OWNER_WAIT_ITEMS.map((item) => item.id));
19602
+ var ownerWaitItemIdSchema = external_exports.enum(OWNER_WAIT_ITEM_IDS);
19603
+ var OWNER_WAIT_FIXED_COUNT = OWNER_WAIT_ITEMS.length;
19604
+ var OWNER_WAIT_ADDITIONS_MAX = 20;
19605
+ var OWNER_WAIT_TEXT_MAX = 240;
19606
+ var ownerWaitItemRefSchema = external_exports.union([
19607
+ external_exports.number().int().min(1),
19608
+ external_exports.string().uuid({ message: "must be a number from 1 to 6, or the id shown beside an added item" })
19609
+ ]);
19610
+ var ownerWaitText = descriptionText(OWNER_WAIT_TEXT_MAX);
19611
+ var ownerWaitAddRequestSchema = external_exports.object({
19612
+ engagementId: external_exports.string().min(1),
19613
+ text: ownerWaitText,
19614
+ reason: ownerWaitText
19615
+ }).strict();
19616
+ var ownerWaitAddResultSchema = external_exports.discriminatedUnion("outcome", [
19617
+ external_exports.object({ outcome: external_exports.literal("added"), id: external_exports.string().uuid(), addedAt: utcIso8601 }).strict(),
19618
+ external_exports.object({ outcome: external_exports.literal("refused"), reason: external_exports.literal("too-many") }).strict()
19619
+ ]);
19620
+ var ownerWaitUndoableSchema = external_exports.enum(["yes", "no"]);
19621
+ var ownerWaitDeclareRequestSchema = external_exports.object({
19622
+ engagementId: external_exports.string().min(1),
19623
+ item: ownerWaitItemRefSchema,
19624
+ question: ownerWaitText,
19625
+ recommendation: ownerWaitText,
19626
+ task: buildTaskIdSchema.optional(),
19627
+ by: agentNameSchema.optional(),
19628
+ undoable: ownerWaitUndoableSchema.optional(),
19629
+ how: statementText(STATEMENT_MAX).optional()
19630
+ }).strict().refine((r) => r.undoable === "yes" === (r.how !== void 0), {
19631
+ message: "says how to undo it exactly when it can be undone",
19632
+ path: ["how"]
19633
+ });
19634
+ var OWNER_WAIT_DECLARE_REFUSALS = ["no-open-phase", "unknown-item", "say-if-undoable"];
19635
+ var ownerWaitDeclareResultSchema = external_exports.discriminatedUnion("outcome", [
19636
+ external_exports.object({ outcome: external_exports.literal("recorded"), id: external_exports.string().uuid(), say: external_exports.string().min(1) }).strict(),
19637
+ external_exports.object({
19638
+ outcome: external_exports.literal("go-ahead"),
19639
+ id: external_exports.string().uuid(),
19640
+ callId: external_exports.number().int().positive(),
19641
+ say: external_exports.string().min(1)
19642
+ }).strict(),
19643
+ external_exports.object({ outcome: external_exports.literal("refused"), reason: external_exports.enum(OWNER_WAIT_DECLARE_REFUSALS) }).strict()
19644
+ ]);
19645
+ var ownerWaitAddedItemSchema = external_exports.object({
19646
+ id: external_exports.string().uuid(),
19647
+ words: external_exports.string().min(1),
19648
+ reason: external_exports.string().min(1),
19649
+ addedAt: utcIso8601
19650
+ }).strict();
19651
+ var ownerWaitListSchema = external_exports.object({
19652
+ fixed: external_exports.array(external_exports.object({ id: ownerWaitItemIdSchema, words: external_exports.string().min(1) }).strict()).length(OWNER_WAIT_FIXED_COUNT),
19653
+ added: external_exports.array(ownerWaitAddedItemSchema).max(OWNER_WAIT_ADDITIONS_MAX)
19654
+ }).strict();
19655
+ var ownerWaitDeclarationSchema = external_exports.object({
19656
+ id: external_exports.string().uuid(),
19657
+ fixedItem: ownerWaitItemIdSchema.nullable(),
19658
+ addition: external_exports.object({ id: external_exports.string().uuid(), words: external_exports.string().min(1) }).strict().nullable(),
19659
+ question: external_exports.string().min(1),
19660
+ recommendation: external_exports.string().min(1),
19661
+ task: buildTaskIdSchema.nullable(),
19662
+ declaredAt: utcIso8601
19663
+ }).strict().refine((d) => d.fixedItem === null !== (d.addition === null), {
19664
+ message: "names exactly one item: a fixed one or an added one",
19665
+ path: ["fixedItem"]
19666
+ });
19667
+ var ownerWaitDeclarationsSchema = external_exports.object({ declarations: external_exports.array(ownerWaitDeclarationSchema) }).strict();
19668
+ var ownerWaitOpenSchema = external_exports.object({
19669
+ wait: ownerWaitShortIdSchema,
19670
+ task: buildTaskIdSchema.nullable(),
19671
+ question: external_exports.string().min(1),
19672
+ recommendation: external_exports.string().min(1),
19673
+ declaredAt: utcIso8601
19674
+ }).strict();
19675
+ var ownerWaitsOpenSchema = external_exports.object({ waits: external_exports.array(ownerWaitOpenSchema) }).strict();
19676
+ var OWNER_WAIT_TOO_MANY = `This project already has ${String(OWNER_WAIT_ADDITIONS_MAX)} things added to what waits for the owner. Ask the owner to take one off on their settings page in the console first; it cannot be done by typing in chat.`;
19389
19677
 
19390
19678
  // ../events/dist/autonomy.js
19391
19679
  var AUTONOMY_CHANGED_IN = ["console", "chat"];
@@ -21569,6 +21857,110 @@ function toAstShapes(markers) {
21569
21857
  // dist/extract/context.js
21570
21858
  import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "node:fs";
21571
21859
  import { join as join4, relative as relative2, sep } from "node:path";
21860
+
21861
+ // dist/extract/repo-facts.js
21862
+ var DATABASE_FILE = /\.sql$|^schema\.prisma$|^drizzle\.config\.|^alembic\.ini$|^knexfile\./i;
21863
+ var BROWSER_FILE = /\.(tsx|jsx|vue|svelte|astro)$|^index\.html$|^(next|vite|nuxt|svelte|astro)\.config\.|^angular\.json$/i;
21864
+ var WEBHOOK_NAME = /webhook/i;
21865
+ var DATABASE_DEPS = /* @__PURE__ */ new Set([
21866
+ "pg",
21867
+ "mysql",
21868
+ "mysql2",
21869
+ "mongodb",
21870
+ "mongoose",
21871
+ "sqlite3",
21872
+ "better-sqlite3",
21873
+ "prisma",
21874
+ "@prisma/client",
21875
+ "drizzle-orm",
21876
+ "typeorm",
21877
+ "sequelize",
21878
+ "knex",
21879
+ "postgres",
21880
+ "psycopg",
21881
+ "psycopg2",
21882
+ "sqlalchemy",
21883
+ "pymysql",
21884
+ "asyncpg",
21885
+ "pymongo",
21886
+ "github.com/lib/pq",
21887
+ "github.com/jackc/pgx",
21888
+ "github.com/go-sql-driver/mysql",
21889
+ "gorm.io/gorm",
21890
+ "diesel",
21891
+ "sqlx",
21892
+ "activerecord"
21893
+ ]);
21894
+ var BROWSER_DEPS = /* @__PURE__ */ new Set([
21895
+ "react",
21896
+ "react-dom",
21897
+ "next",
21898
+ "vue",
21899
+ "nuxt",
21900
+ "svelte",
21901
+ "@sveltejs/kit",
21902
+ "@angular/core",
21903
+ "solid-js",
21904
+ "preact",
21905
+ "astro",
21906
+ "vite"
21907
+ ]);
21908
+ var MANIFEST_NAMES = /* @__PURE__ */ new Set(["package.json", "go.mod", "requirements.txt", "pyproject.toml", "Gemfile", "Cargo.toml"]);
21909
+ function isFactManifest(fileName) {
21910
+ return MANIFEST_NAMES.has(fileName);
21911
+ }
21912
+ function newFactCollector() {
21913
+ return { found: /* @__PURE__ */ new Set() };
21914
+ }
21915
+ function noteFileName(c, fileName, underDocs) {
21916
+ if (underDocs)
21917
+ return;
21918
+ if (DATABASE_FILE.test(fileName))
21919
+ c.found.add("database");
21920
+ if (BROWSER_FILE.test(fileName))
21921
+ c.found.add("browser-app");
21922
+ if (WEBHOOK_NAME.test(fileName))
21923
+ c.found.add("inbound-webhooks");
21924
+ }
21925
+ function noteDirName(c, dirName, underDocs) {
21926
+ if (underDocs)
21927
+ return;
21928
+ if (WEBHOOK_NAME.test(dirName))
21929
+ c.found.add("inbound-webhooks");
21930
+ if (dirName === "migrations")
21931
+ c.found.add("database");
21932
+ }
21933
+ function dependencyNames(fileName, content) {
21934
+ if (fileName === "package.json") {
21935
+ try {
21936
+ const parsed = JSON.parse(content);
21937
+ const names = [];
21938
+ for (const key of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
21939
+ const block = parsed[key];
21940
+ if (block !== null && typeof block === "object")
21941
+ names.push(...Object.keys(block));
21942
+ }
21943
+ return names;
21944
+ } catch {
21945
+ return [];
21946
+ }
21947
+ }
21948
+ return content.split(/[\s"'=<>~^,;:\[\]()]+/).filter((t) => t.length > 0);
21949
+ }
21950
+ function noteManifest(c, fileName, content) {
21951
+ for (const name of dependencyNames(fileName, content)) {
21952
+ const lower = name.toLowerCase();
21953
+ if (DATABASE_DEPS.has(lower))
21954
+ c.found.add("database");
21955
+ if (BROWSER_DEPS.has(lower))
21956
+ c.found.add("browser-app");
21957
+ }
21958
+ }
21959
+ function factsOf(c) {
21960
+ return REPOSITORY_FACTS.filter((f) => c.found.has(f));
21961
+ }
21962
+
21963
+ // dist/extract/context.js
21572
21964
  var EXCLUDED_DIRS2 = /* @__PURE__ */ new Set([
21573
21965
  "node_modules",
21574
21966
  ".git",
@@ -21582,6 +21974,7 @@ var EXCLUDED_DIRS2 = /* @__PURE__ */ new Set([
21582
21974
  "coverage",
21583
21975
  ".cache"
21584
21976
  ]);
21977
+ var PROSE_DIRS = /* @__PURE__ */ new Set(["docs", "skills", "runbooks", "method"]);
21585
21978
  function classifyFile(fileName) {
21586
21979
  if (fileName === "Dockerfile" || fileName.startsWith("Dockerfile."))
21587
21980
  return "dockerfile";
@@ -21591,7 +21984,7 @@ function classifyFile(fileName) {
21591
21984
  return "env-example";
21592
21985
  return null;
21593
21986
  }
21594
- function walk(dir, repoRoot, acc) {
21987
+ function walk(dir, repoRoot, acc, facts, underDocs = false) {
21595
21988
  let entries;
21596
21989
  try {
21597
21990
  entries = readdirSync2(dir, { withFileTypes: true });
@@ -21602,11 +21995,21 @@ function walk(dir, repoRoot, acc) {
21602
21995
  if (entry.isDirectory()) {
21603
21996
  if (EXCLUDED_DIRS2.has(entry.name))
21604
21997
  continue;
21605
- walk(join4(dir, entry.name), repoRoot, acc);
21998
+ const lower = entry.name.toLowerCase();
21999
+ noteDirName(facts, lower, underDocs);
22000
+ walk(join4(dir, entry.name), repoRoot, acc, facts, underDocs || PROSE_DIRS.has(lower));
21606
22001
  continue;
21607
22002
  }
21608
22003
  if (!entry.isFile())
21609
22004
  continue;
22005
+ noteFileName(facts, entry.name, underDocs);
22006
+ if (!underDocs && isFactManifest(entry.name)) {
22007
+ try {
22008
+ noteManifest(facts, entry.name, readFileSync4(join4(dir, entry.name), "utf-8"));
22009
+ } catch {
22010
+ continue;
22011
+ }
22012
+ }
21610
22013
  const kind = classifyFile(entry.name);
21611
22014
  if (!kind)
21612
22015
  continue;
@@ -21626,8 +22029,9 @@ function loadChangeSetContext(repoRoot) {
21626
22029
  if (cached2)
21627
22030
  return cached2;
21628
22031
  const declarationFiles = [];
21629
- walk(repoRoot, repoRoot, declarationFiles);
21630
- const context = { declarationFiles, repoRoot };
22032
+ const facts = newFactCollector();
22033
+ walk(repoRoot, repoRoot, declarationFiles, facts);
22034
+ const context = { declarationFiles, repoRoot, repoFacts: factsOf(facts) };
21631
22035
  cache.set(repoRoot, context);
21632
22036
  return context;
21633
22037
  }
@@ -21722,6 +22126,7 @@ function buildGuardEvalRun(opts) {
21722
22126
  ...opts.failureReason !== void 0 ? { failureReason: opts.failureReason } : {},
21723
22127
  phase: opts.phase,
21724
22128
  scope,
22129
+ ...opts.repoFacts !== void 0 ? { repoFacts: [...opts.repoFacts] } : {},
21725
22130
  // cost is optional (D6: live cost signal lands later; ingest works without it)
21726
22131
  // result present IFF the run was evaluated (schema-enforced refinement).
21727
22132
  ...opts.outcome === "evaluated" && opts.result !== void 0 ? { result: opts.result } : {}
@@ -21741,7 +22146,8 @@ async function emitRunRecord(opts) {
21741
22146
  outcome: opts.outcome,
21742
22147
  failureReason: opts.failureReason,
21743
22148
  changeSet: opts.changeSet,
21744
- diffBase: opts.diffBase
22149
+ diffBase: opts.diffBase,
22150
+ repoFacts: opts.repoFacts
21745
22151
  });
21746
22152
  await emitTelemetry(run, {
21747
22153
  controlTelemetryUrl: telemetryConfig.controlTelemetryUrl,
@@ -21807,7 +22213,8 @@ async function runCi() {
21807
22213
  runType: "ci",
21808
22214
  outcome: acquired.kind === "contract" ? "contract-error" : "infra-error",
21809
22215
  failureReason: `ci credential: ${acquired.kind}: ${acquired.message}`,
21810
- changeSet
22216
+ changeSet,
22217
+ repoFacts: context.repoFacts
21811
22218
  });
21812
22219
  process.stderr.write(credentialExchangeFailureMessage(acquired));
21813
22220
  return 1;
@@ -21822,7 +22229,7 @@ async function runCi() {
21822
22229
  }
21823
22230
  }
21824
22231
  if (!configResult.config) {
21825
- await emitRunRecord({ runType: "ci", outcome: "unconfigured", changeSet });
22232
+ await emitRunRecord({ runType: "ci", outcome: "unconfigured", changeSet, repoFacts: context.repoFacts });
21826
22233
  process.stderr.write(isHalfcycleOwnCi() ? ciNeverConfiguredMessage() : CI_NOT_SUPPORTED_MESSAGE);
21827
22234
  return 1;
21828
22235
  }
@@ -21839,6 +22246,7 @@ async function evaluateAndAct(args2) {
21839
22246
  outcome: outcomeForClientFailure(clientResult.kind),
21840
22247
  failureReason: failure.reason,
21841
22248
  changeSet,
22249
+ repoFacts: context.repoFacts,
21842
22250
  credentialRefused: failure.kind === "credential"
21843
22251
  });
21844
22252
  if (failure.kind === "credential") {
@@ -21865,6 +22273,7 @@ async function evaluateAndAct(args2) {
21865
22273
  phase: telemetryConfig.phase,
21866
22274
  outcome: "evaluated",
21867
22275
  changeSet,
22276
+ repoFacts: context.repoFacts,
21868
22277
  result: clientResult.envelope
21869
22278
  });
21870
22279
  await emitTelemetry(run, {
@@ -24230,19 +24639,19 @@ function settled(dir, file2, tokens) {
24230
24639
  writePhaseLimit(dir, next);
24231
24640
  return next;
24232
24641
  }
24233
- function turnEndLimitLine(file2, tokens, working) {
24642
+ function turnEndLimitLine(file2, tokens, working, weekCeilingTokens = null) {
24234
24643
  if (file2.awaiting) {
24235
24644
  if (file2.shown.includes("limit"))
24236
24645
  return { text: null, file: file2 };
24237
24646
  return {
24238
- text: limitReachedLineOf(tokens, file2.limit, working, estimateOf(file2)),
24647
+ text: limitReachedLineOf({ limit: file2.limit, working, weekCeilingTokens }),
24239
24648
  file: { ...file2, shown: [...LIMIT_PARTS] }
24240
24649
  };
24241
24650
  }
24242
24651
  const r = partsReached(tokens, file2.limit, file2.shown);
24243
24652
  if (r.show === null)
24244
24653
  return { text: null, file: file2 };
24245
- return { text: limitPartLineOf(r.show, tokens, file2.limit), file: { ...file2, shown: r.shown } };
24654
+ return { text: limitPartLineOf(r.show, tokens, weekCeilingTokens), file: { ...file2, shown: r.shown } };
24246
24655
  }
24247
24656
  function openPhaseLimit(dir, remembered) {
24248
24657
  if (remembered !== null && remembered.phase === null)
@@ -26481,7 +26890,7 @@ async function evaluateEdit(reply, readSeat, at, profile) {
26481
26890
  });
26482
26891
  const configResult = readConfig();
26483
26892
  if (!configResult.config) {
26484
- await emitRunRecord({ runType: "hook", outcome: "unconfigured", changeSet });
26893
+ await emitRunRecord({ runType: "hook", outcome: "unconfigured", changeSet, repoFacts: context.repoFacts });
26485
26894
  if (!firstNoticeThisSession("credential-missing", repoRoot, input.session_id))
26486
26895
  return;
26487
26896
  emitNeverConfigured(UNCONFIGURED_EXIT_TOOL_USE, readNeverConfiguredReason(), pinnedEngagementId(projectFolder()), reply);
@@ -26505,6 +26914,7 @@ async function evaluateEdit(reply, readSeat, at, profile) {
26505
26914
  outcome: outcomeForClientFailure(clientResult.kind),
26506
26915
  failureReason: failure.reason,
26507
26916
  changeSet,
26917
+ repoFacts: context.repoFacts,
26508
26918
  credential: evaluation.token,
26509
26919
  credentialRefused: failure.kind === "credential"
26510
26920
  });
@@ -26539,6 +26949,7 @@ async function evaluateEdit(reply, readSeat, at, profile) {
26539
26949
  phase: telemetryConfig.phase,
26540
26950
  outcome: "evaluated",
26541
26951
  changeSet,
26952
+ repoFacts: context.repoFacts,
26542
26953
  result: clientResult.envelope
26543
26954
  });
26544
26955
  await emitTelemetry(run, {
@@ -26786,29 +27197,171 @@ function runsBesideOtherWork(toolInput, record2) {
26786
27197
  }
26787
27198
 
26788
27199
  // dist/pace-hook.js
26789
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync20, renameSync as renameSync10, unlinkSync as unlinkSync8, writeFileSync as writeFileSync12 } from "node:fs";
26790
- import nodePath5 from "node:path";
27200
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync21, renameSync as renameSync11, unlinkSync as unlinkSync8, writeFileSync as writeFileSync13 } from "node:fs";
27201
+ import nodePath6 from "node:path";
26791
27202
 
26792
27203
  // dist/pace-verb.js
26793
- import { mkdirSync as mkdirSync7, readdirSync as readdirSync8, readFileSync as readFileSync19, renameSync as renameSync9, statSync as statSync6, writeFileSync as writeFileSync11 } from "node:fs";
27204
+ import { mkdirSync as mkdirSync8, readdirSync as readdirSync8, readFileSync as readFileSync20, renameSync as renameSync10, statSync as statSync6, writeFileSync as writeFileSync12 } from "node:fs";
26794
27205
  import { homedir as homedir9 } from "node:os";
27206
+ import nodePath5 from "node:path";
27207
+
27208
+ // dist/plan-report.js
27209
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync19, renameSync as renameSync9, writeFileSync as writeFileSync11 } from "node:fs";
26795
27210
  import nodePath4 from "node:path";
26796
- var PACE_DEFAULT_TASK_MINUTES = 30;
26797
- var PACE_DURATION_ROUND_MINUTES = 10;
27211
+ var PLAN_REPORT_MIN_INTERVAL_MS = 10 * 60 * 1e3;
27212
+ var PLAN_MIN_GAIN_POINTS = 2;
27213
+ var PLAN_FIRST_READ_FILENAME = "plan-first-read.json";
27214
+ var PLAN_DERIVED_FILENAME = "plan-derived-ceiling.json";
27215
+ var PLAN_REPORT_THROTTLE_FILENAME = "plan-report.json";
27216
+ function isNum2(v) {
27217
+ return typeof v === "number" && Number.isFinite(v);
27218
+ }
27219
+ function isTime(v) {
27220
+ return typeof v === "string" && !Number.isNaN(Date.parse(v));
27221
+ }
27222
+ function readAccounts(path) {
27223
+ try {
27224
+ const v = JSON.parse(readFileSync19(path, "utf-8"));
27225
+ const accounts = typeof v === "object" && v !== null ? v["accounts"] : void 0;
27226
+ return typeof accounts === "object" && accounts !== null && !Array.isArray(accounts) ? { ...accounts } : {};
27227
+ } catch {
27228
+ return {};
27229
+ }
27230
+ }
27231
+ function writeAccount(path, key, entry) {
27232
+ const accounts = readAccounts(path);
27233
+ accounts[key] = entry;
27234
+ mkdirSync7(nodePath4.dirname(path), { recursive: true, mode: 448 });
27235
+ const tmp = `${path}.${String(process.pid)}.${Math.random().toString(36).slice(2, 10)}.tmp`;
27236
+ writeFileSync11(tmp, JSON.stringify({ accounts }), { mode: 384 });
27237
+ renameSync9(tmp, path);
27238
+ }
27239
+ function firstReadOf(key, home) {
27240
+ const v = readAccounts(nodePath4.join(pacingDir(home), PLAN_FIRST_READ_FILENAME))[key];
27241
+ if (typeof v !== "object" || v === null)
27242
+ return null;
27243
+ const o = v;
27244
+ return isNum2(o["usedPercent"]) && isTime(o["resetsAt"]) && isTime(o["at"]) ? { usedPercent: o["usedPercent"], resetsAt: o["resetsAt"], at: o["at"] } : null;
27245
+ }
27246
+ function derivedOf(key, home) {
27247
+ const v = readAccounts(nodePath4.join(pacingDir(home), PLAN_DERIVED_FILENAME))[key];
27248
+ if (typeof v !== "object" || v === null)
27249
+ return null;
27250
+ const o = v;
27251
+ return isNum2(o["tokens"]) && Number.isInteger(o["tokens"]) && o["tokens"] > 0 && isTime(o["resetsAt"]) ? { tokens: o["tokens"], resetsAt: o["resetsAt"] } : null;
27252
+ }
27253
+ function weeklyCeilingOf(key, deps = {}) {
27254
+ const learned = readLearnedAccount(key, deps)?.ceilings["seven-day"];
27255
+ if (learned !== void 0)
27256
+ return learned.tokens;
27257
+ const week = readReportedAccount(key, deps)["seven-day"];
27258
+ const derived = derivedOf(key, deps.home);
27259
+ if (week === void 0 || derived === null)
27260
+ return null;
27261
+ return Date.parse(derived.resetsAt) === Date.parse(week.resetsAt) ? derived.tokens : null;
27262
+ }
27263
+ function derivedBetweenReads(key, week, deps, nowMs) {
27264
+ const first = firstReadOf(key, deps.home);
27265
+ if (first === null || Date.parse(first.resetsAt) !== Date.parse(week.resetsAt)) {
27266
+ const thisRead = { usedPercent: week.usedPercentage, resetsAt: week.resetsAt, at: week.savedAt };
27267
+ writeAccount(nodePath4.join(pacingDir(deps.home), PLAN_FIRST_READ_FILENAME), key, thisRead);
27268
+ return null;
27269
+ }
27270
+ if (deps.configFolder === "codex-home")
27271
+ return null;
27272
+ const gain = week.usedPercentage - first.usedPercent;
27273
+ if (gain < PLAN_MIN_GAIN_POINTS)
27274
+ return null;
27275
+ if (readLearnedAccount(key, { home: deps.home }) === null)
27276
+ return null;
27277
+ const scanned = scanUnderLock(key, { ...deps.home === void 0 ? {} : { home: deps.home }, now: () => new Date(nowMs) }, 0);
27278
+ if (scanned === null)
27279
+ return null;
27280
+ const fromSec = Math.floor(Date.parse(first.at) / 1e3);
27281
+ const toSec = Math.floor(Date.parse(week.savedAt) / 1e3);
27282
+ if (!(toSec > fromSec) || scanned.coveredFrom > fromSec)
27283
+ return null;
27284
+ const tokens = tokensBetween(scanned.minutes, fromSec, toSec);
27285
+ if (!(tokens > 0))
27286
+ return null;
27287
+ const ceiling = Math.round(tokens * 100 / gain);
27288
+ return ceiling > 0 ? ceiling : null;
27289
+ }
27290
+ async function planUsageReport(input, deps = {}) {
27291
+ try {
27292
+ const code = process.exitCode;
27293
+ if (code !== void 0 && code !== null && Number(code) !== 0)
27294
+ return;
27295
+ const env = deps.env ?? process.env;
27296
+ const configured = readConfig(env);
27297
+ if (configured.config === null)
27298
+ return;
27299
+ const { guardEngagementId: engagementId, guardServiceToken: token } = configured.config;
27300
+ const dir = enginePacingDir(engagementId, deps.home);
27301
+ if (dir === null)
27302
+ return;
27303
+ const nowMs = (deps.now ?? Date.now)();
27304
+ const said = typeof input === "object" && input !== null ? input["transcript_path"] : void 0;
27305
+ const key = configFolderKey({
27306
+ env,
27307
+ transcriptPath: typeof said === "string" && said !== "" ? said : null,
27308
+ home: deps.home,
27309
+ rule: deps.configFolder
27310
+ });
27311
+ const throttlePath = nodePath4.join(dir, PLAN_REPORT_THROTTLE_FILENAME);
27312
+ const last = readAccounts(throttlePath)[key];
27313
+ if (isNum2(last) && nowMs >= last && nowMs - last < PLAN_REPORT_MIN_INTERVAL_MS)
27314
+ return;
27315
+ writeAccount(throttlePath, key, nowMs);
27316
+ const now = new Date(nowMs);
27317
+ if (deps.configFolder === "codex-home")
27318
+ refreshCodexWindows(key, { home: deps.home, now: () => now });
27319
+ const reported = readReportedAccount(key, { home: deps.home });
27320
+ const five = reported["five-hour"];
27321
+ const week = reported["seven-day"];
27322
+ const freshWeek = reportedFresh(week, now) ? week : void 0;
27323
+ let ceiling = null;
27324
+ if (freshWeek !== void 0) {
27325
+ const learned = readLearnedAccount(key, { home: deps.home })?.ceilings["seven-day"];
27326
+ ceiling = weeklyCeilingOf(key, { home: deps.home });
27327
+ let derived = null;
27328
+ if (learned === void 0) {
27329
+ try {
27330
+ derived = derivedBetweenReads(key, freshWeek, deps, nowMs);
27331
+ } catch {
27332
+ derived = null;
27333
+ }
27334
+ }
27335
+ if (derived !== null) {
27336
+ try {
27337
+ writeAccount(nodePath4.join(pacingDir(deps.home), PLAN_DERIVED_FILENAME), key, { tokens: derived, resetsAt: freshWeek.resetsAt });
27338
+ } catch {
27339
+ }
27340
+ ceiling = derived;
27341
+ }
27342
+ }
27343
+ const report = {
27344
+ fiveHour: reportedFresh(five, now) ? { usedPercent: five.usedPercentage, resetsAt: five.resetsAt } : null,
27345
+ week: freshWeek === void 0 ? null : { usedPercent: freshWeek.usedPercentage, resetsAt: freshWeek.resetsAt, ceilingTokens: ceiling },
27346
+ readAt: now.toISOString()
27347
+ };
27348
+ if (!planUsageReportSchema.safeParse(report).success)
27349
+ return;
27350
+ await requestJson(deps.fetchFn ?? globalThis.fetch, `${readControlOrigin(env).origin}/engagements/${encodeURIComponent(engagementId)}/plan-usage`, {
27351
+ method: "PUT",
27352
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
27353
+ body: JSON.stringify(report)
27354
+ }, OBSERVE_REQUEST_DEADLINE_MS);
27355
+ } catch {
27356
+ }
27357
+ }
27358
+
27359
+ // dist/pace-verb.js
26798
27360
  var PACE_WINDOW_MINUTES = 300;
26799
27361
  var PACE_BASIS_DEADLINE_MS = 5e3;
26800
27362
  var PACE_FIRST_READ_WAIT_MS = 10 * 60 * 1e3;
26801
27363
  var PACE_LATER_READ_WAIT_MS = 5e3;
26802
27364
  var BUILD_NUMBER_FILENAME = "build.json";
26803
- function taskMinutesOf(task, medianTaskMinutes) {
26804
- if (task.human)
26805
- return 0;
26806
- if (task.budgetMinutes !== void 0)
26807
- return task.budgetMinutes;
26808
- if (medianTaskMinutes !== null && medianTaskMinutes > 0)
26809
- return medianTaskMinutes;
26810
- return PACE_DEFAULT_TASK_MINUTES;
26811
- }
26812
27365
  function layoutGraph(tasks, agents, medianTaskMinutes) {
26813
27366
  const index = /* @__PURE__ */ new Map();
26814
27367
  tasks.forEach((t2, i) => {
@@ -26892,7 +27445,7 @@ function suggestedNumberOf(input) {
26892
27445
  function suggestPace(input) {
26893
27446
  const tasks = input.tasks !== null && input.tasks.length > 0 ? input.tasks : null;
26894
27447
  const number4 = input.named !== null ? clampAgents(input.named, PACE_AGENTS_MAX) : suggestedNumberOf(input);
26895
- const minutes = tasks === null ? null : Math.ceil(layoutGraph(tasks, number4, input.medianTaskMinutes).minutes / PACE_DURATION_ROUND_MINUTES) * PACE_DURATION_ROUND_MINUTES;
27448
+ const minutes = tasks === null ? null : paceDurationRoundedOf(layoutGraph(tasks, number4, input.medianTaskMinutes).minutes);
26896
27449
  let pauses = 0;
26897
27450
  const ceiling = input.learned?.ceilings["five-hour"];
26898
27451
  if (tasks !== null && ceiling !== void 0 && input.medianTaskTokens !== null) {
@@ -26908,20 +27461,32 @@ function suggestPace(input) {
26908
27461
  minutes,
26909
27462
  pauses,
26910
27463
  keepFreePercent: input.keepFreePercent,
26911
- knownWindow
27464
+ knownWindow,
27465
+ workMinutes: agentWorkMinutesOf(tasks, input.medianTaskMinutes)
26912
27466
  };
26913
27467
  }
27468
+ function weekCeilingFor(key, home) {
27469
+ try {
27470
+ const tokens = weeklyCeilingOf(key, home === void 0 ? {} : { home });
27471
+ if (tokens === null || !Number.isFinite(tokens))
27472
+ return null;
27473
+ const whole = Math.round(tokens);
27474
+ return whole > 0 ? whole : null;
27475
+ } catch {
27476
+ return null;
27477
+ }
27478
+ }
26914
27479
  function enginePacingDir(engagementId, home) {
26915
27480
  if (!isEngagementId(engagementId))
26916
27481
  return null;
26917
- return nodePath4.join(home ?? homedir9(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
27482
+ return nodePath5.join(home ?? homedir9(), HALFCYCLE_DIR_NAME, ENGAGEMENTS_DIR_NAME, engagementId, PACING_DIR_NAME);
26918
27483
  }
26919
27484
  function readBuildNumber(engagementId, home) {
26920
27485
  const dir = enginePacingDir(engagementId, home);
26921
27486
  if (dir === null)
26922
27487
  return null;
26923
27488
  try {
26924
- const v = JSON.parse(readFileSync19(nodePath4.join(dir, BUILD_NUMBER_FILENAME), "utf-8"));
27489
+ const v = JSON.parse(readFileSync20(nodePath5.join(dir, BUILD_NUMBER_FILENAME), "utf-8"));
26925
27490
  const n = v["number"];
26926
27491
  if (typeof v["phase"] === "string" && typeof n === "number" && Number.isInteger(n) && n >= 1 && n <= PACE_AGENTS_MAX && typeof v["namedByOwner"] === "boolean") {
26927
27492
  return { phase: v["phase"], number: n, namedByOwner: v["namedByOwner"] };
@@ -26938,11 +27503,11 @@ function writeBuildNumber(engagementId, value, home) {
26938
27503
  const dir = enginePacingDir(engagementId, home);
26939
27504
  if (dir === null)
26940
27505
  throw new Error("not an engagement id");
26941
- mkdirSync7(dir, { recursive: true, mode: 448 });
26942
- const path = nodePath4.join(dir, BUILD_NUMBER_FILENAME);
27506
+ mkdirSync8(dir, { recursive: true, mode: 448 });
27507
+ const path = nodePath5.join(dir, BUILD_NUMBER_FILENAME);
26943
27508
  const tmp = `${path}.${String(process.pid)}.tmp`;
26944
- writeFileSync11(tmp, JSON.stringify(value), { mode: 384 });
26945
- renameSync9(tmp, path);
27509
+ writeFileSync12(tmp, JSON.stringify(value), { mode: 384 });
27510
+ renameSync10(tmp, path);
26946
27511
  }
26947
27512
  function liveSubAgentsIn(cwd, deps = {}) {
26948
27513
  try {
@@ -26958,7 +27523,7 @@ function liveSubAgentsIn(cwd, deps = {}) {
26958
27523
  if (p === null || p.key !== key || p.agentId === null)
26959
27524
  continue;
26960
27525
  try {
26961
- if (now - statSync6(nodePath4.join(dir, name)).mtimeMs <= LIVE_WINDOW_MS)
27526
+ if (now - statSync6(nodePath5.join(dir, name)).mtimeMs <= LIVE_WINDOW_MS)
26962
27527
  n += 1;
26963
27528
  } catch {
26964
27529
  }
@@ -27109,6 +27674,8 @@ async function runPaceVerb(args2, deps = {}) {
27109
27674
  refreshCodexWindows(key, scanDeps);
27110
27675
  const learned = codex ? null : readThisAccount(key, scanDeps, stderr);
27111
27676
  const reported = readReportedAccount(key, scanDeps);
27677
+ const weekCeilingTokens = weekCeilingFor(key, deps.home);
27678
+ const ceiling = weekCeilingTokens === null ? {} : { weekCeilingTokens };
27112
27679
  let number4;
27113
27680
  let suggestion;
27114
27681
  let limitFile = null;
@@ -27131,7 +27698,7 @@ async function runPaceVerb(args2, deps = {}) {
27131
27698
  return { stdout: null, stderr: [...stderr, PACE_ONLY_PERSON_RAISES], code: 2 };
27132
27699
  }
27133
27700
  const named = parsed.agents ?? kept;
27134
- suggestion = named === null ? suggested : suggestFor(named);
27701
+ suggestion = { ...named === null ? suggested : suggestFor(named), ...ceiling };
27135
27702
  number4 = suggestion.number;
27136
27703
  writeBuildNumber(engagementId, { phase: parsed.phase, number: number4, namedByOwner: named !== null }, deps.home);
27137
27704
  const limitBasis = basis === null ? null : { tasks: sameGraph ? basis.tasks : [], medianTaskTokens: basis.medianTaskTokens };
@@ -27185,14 +27752,15 @@ async function runPaceVerb(args2, deps = {}) {
27185
27752
  ...decision.until !== null ? { until: decision.until.toISOString() } : {},
27186
27753
  keepFreePercent,
27187
27754
  ...suggestion !== void 0 ? { suggestion } : {},
27188
- ...limitFile !== null ? { usage: { used: readPhaseUsage(pacingDir2, limitFile.phase), limit: limitFile.limit } } : {},
27755
+ ...limitFile !== null ? { usage: { used: readPhaseUsage(pacingDir2, limitFile.phase), limit: limitFile.limit, ...ceiling } } : {},
27189
27756
  ...atLimit ? {
27190
27757
  limit: "reached",
27191
27758
  limitHeld: {
27192
27759
  used: readPhaseUsage(pacingDir2, limitFile.phase),
27193
27760
  limit: limitFile.limit,
27194
27761
  raiseBy: estimateOf(limitFile),
27195
- working: live
27762
+ working: live,
27763
+ ...ceiling
27196
27764
  }
27197
27765
  } : {}
27198
27766
  };
@@ -27217,17 +27785,17 @@ function objectOf2(v) {
27217
27785
  }
27218
27786
  function readJson(path) {
27219
27787
  try {
27220
- return objectOf2(JSON.parse(readFileSync20(path, "utf-8")));
27788
+ return objectOf2(JSON.parse(readFileSync21(path, "utf-8")));
27221
27789
  } catch {
27222
27790
  return null;
27223
27791
  }
27224
27792
  }
27225
27793
  function writeJsonWhole2(path, value) {
27226
- mkdirSync8(nodePath5.dirname(path), { recursive: true, mode: 448 });
27794
+ mkdirSync9(nodePath6.dirname(path), { recursive: true, mode: 448 });
27227
27795
  const tmp = `${path}.${String(process.pid)}.${Math.random().toString(36).slice(2, 10)}.tmp`;
27228
27796
  try {
27229
- writeFileSync12(tmp, JSON.stringify(value), { mode: 384 });
27230
- renameSync10(tmp, path);
27797
+ writeFileSync13(tmp, JSON.stringify(value), { mode: 384 });
27798
+ renameSync11(tmp, path);
27231
27799
  } catch (err) {
27232
27800
  try {
27233
27801
  unlinkSync8(tmp);
@@ -27340,7 +27908,7 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
27340
27908
  return;
27341
27909
  }
27342
27910
  const working = liveCount(readLiveness(field2(input, "session_id"), field2(input, "cwd") ?? process.cwd(), deps.liveness));
27343
- const limitText = limitAtTurnEnd(dir, basis.phase, read !== null && read.phase === basis.phase ? { tasks: read.tasks, medianTaskTokens: read.medianTaskTokens } : null, working);
27911
+ const limitText = limitAtTurnEnd(dir, basis.phase, read !== null && read.phase === basis.phase ? { tasks: read.tasks, medianTaskTokens: read.medianTaskTokens } : null, working, key, deps.home);
27344
27912
  let learned = null;
27345
27913
  if (deps.configFolder === "codex-home") {
27346
27914
  refreshCodexWindows(key, { home: deps.home, now: () => now });
@@ -27390,13 +27958,13 @@ async function paceAtTurnEnd(input, reply, deps = {}) {
27390
27958
  } catch {
27391
27959
  }
27392
27960
  }
27393
- function limitAtTurnEnd(dir, phase, basis, working) {
27961
+ function limitAtTurnEnd(dir, phase, basis, working, key, home) {
27394
27962
  try {
27395
27963
  const tokens = readPhaseUsage(dir, phase);
27396
27964
  const file2 = limitInForce(dir, phase, basis, tokens);
27397
27965
  if (file2 === null)
27398
27966
  return null;
27399
- const step = turnEndLimitLine(file2, tokens, working);
27967
+ const step = turnEndLimitLine(file2, tokens, working, weekCeilingFor(key, home));
27400
27968
  if (step.text === null)
27401
27969
  return null;
27402
27970
  writePhaseLimit(dir, step.file);
@@ -27407,7 +27975,7 @@ function limitAtTurnEnd(dir, phase, basis, working) {
27407
27975
  }
27408
27976
  function readStarts(dir) {
27409
27977
  const out = {};
27410
- for (const [key, sessions] of Object.entries(objectOf2(readJson(nodePath5.join(dir, PACE_STARTS_FILENAME))?.["accounts"]) ?? {})) {
27978
+ for (const [key, sessions] of Object.entries(objectOf2(readJson(nodePath6.join(dir, PACE_STARTS_FILENAME))?.["accounts"]) ?? {})) {
27411
27979
  const s = {};
27412
27980
  for (const [session, list] of Object.entries(objectOf2(sessions) ?? {})) {
27413
27981
  if (Array.isArray(list))
@@ -27435,7 +28003,7 @@ function sleepSync3(ms) {
27435
28003
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
27436
28004
  }
27437
28005
  function underStartsLock(dir, giveUpMs, fn) {
27438
- const lock = nodePath5.join(dir, PACE_STARTS_LOCK_FILENAME);
28006
+ const lock = nodePath6.join(dir, PACE_STARTS_LOCK_FILENAME);
27439
28007
  const giveUpAt = Date.now() + giveUpMs;
27440
28008
  while (!tryTakeLockFile(lock, PACE_STARTS_LOCK_STALE_MS)) {
27441
28009
  if (Date.now() >= giveUpAt)
@@ -27473,7 +28041,7 @@ function retirePendingStart(input, deps = {}) {
27473
28041
  if (list === void 0 || list.length === 0)
27474
28042
  return false;
27475
28043
  list.shift();
27476
- writeJsonWhole2(nodePath5.join(where.dir, PACE_STARTS_FILENAME), { accounts: pruneStarts(file2, nowMs) });
28044
+ writeJsonWhole2(nodePath6.join(where.dir, PACE_STARTS_FILENAME), { accounts: pruneStarts(file2, nowMs) });
27477
28045
  return true;
27478
28046
  }) ?? false;
27479
28047
  } catch {
@@ -27492,7 +28060,7 @@ function paceHoldText(input, toolInput, deps = {}) {
27492
28060
  const beside = () => runsBesideOtherWork(toolInput, withoutAgent(record2, field2(input, "agent_id")));
27493
28061
  const atLimit = openPhaseLimit(where.dir, stateFile.basis);
27494
28062
  if (atLimit !== null && atLimit.awaiting && beside())
27495
- return limitHeldStartTextOf(atLimit.limit);
28063
+ return limitHeldStartTextOf(atLimit.limit, weekCeilingFor(where.key, deps.home));
27496
28064
  const entry = stateFile.accounts[where.key];
27497
28065
  if (entry === void 0)
27498
28066
  return null;
@@ -27525,7 +28093,7 @@ function paceHoldText(input, toolInput, deps = {}) {
27525
28093
  if (live + list.length >= allowance)
27526
28094
  return true;
27527
28095
  const sessions = { ...file2[where.key] ?? {}, [session]: [...list, nowMs] };
27528
- writeJsonWhole2(nodePath5.join(where.dir, PACE_STARTS_FILENAME), { accounts: { ...file2, [where.key]: sessions } });
28096
+ writeJsonWhole2(nodePath6.join(where.dir, PACE_STARTS_FILENAME), { accounts: { ...file2, [where.key]: sessions } });
27529
28097
  return false;
27530
28098
  });
27531
28099
  return pending === true ? held() : null;
@@ -27536,12 +28104,12 @@ function paceHoldText(input, toolInput, deps = {}) {
27536
28104
 
27537
28105
  // dist/risky/account-names.js
27538
28106
  import { randomBytes } from "node:crypto";
27539
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync22, renameSync as renameSync11, rmSync as rmSync3, statSync as statSync8, writeFileSync as writeFileSync13 } from "node:fs";
28107
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync23, renameSync as renameSync12, rmSync as rmSync3, statSync as statSync8, writeFileSync as writeFileSync14 } from "node:fs";
27540
28108
  import { homedir as homedir11 } from "node:os";
27541
28109
  import { isAbsolute as isAbsolute5, join as join16, posix as posix2, resolve as resolve7, sep as sep6 } from "node:path";
27542
28110
 
27543
28111
  // dist/risky/facts.js
27544
- import { existsSync as existsSync8, lstatSync as lstatSync3, readdirSync as readdirSync9, readFileSync as readFileSync21, realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
28112
+ import { existsSync as existsSync8, lstatSync as lstatSync3, readdirSync as readdirSync9, readFileSync as readFileSync22, realpathSync as realpathSync7, statSync as statSync7 } from "node:fs";
27545
28113
  import { homedir as homedir10, tmpdir as tmpdir2 } from "node:os";
27546
28114
  import { basename as pathBasename, dirname as dirname6, isAbsolute as isAbsolute4, join as join15, relative as relative7, resolve as resolve6, sep as sep5 } from "node:path";
27547
28115
 
@@ -29779,7 +30347,7 @@ var Env = class {
29779
30347
  return this.description;
29780
30348
  this.description = null;
29781
30349
  try {
29782
- const raw = JSON.parse(readFileSync21(join15(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
30350
+ const raw = JSON.parse(readFileSync22(join15(repoRoot, SYSTEM_DESCRIPTION_REL), "utf-8"));
29783
30351
  const checked = checkSystemDescription(raw);
29784
30352
  if (checked.ok)
29785
30353
  this.description = checked.description;
@@ -30965,7 +31533,7 @@ function publishFacts(m, env) {
30965
31533
  const start = startFolder(m, env);
30966
31534
  if (start !== null && PACKAGE_JSON_PROGRAMS.has(m.program)) {
30967
31535
  try {
30968
- const pkg = JSON.parse(readFileSync21(join15(start, "package.json"), "utf-8"));
31536
+ const pkg = JSON.parse(readFileSync22(join15(start, "package.json"), "utf-8"));
30969
31537
  if (typeof pkg["name"] === "string" && typeof pkg["version"] === "string" && pkg["name"] !== "" && pkg["version"] !== "") {
30970
31538
  name = pkg["name"];
30971
31539
  version2 = pkg["version"];
@@ -31015,7 +31583,7 @@ function shrinkRecordFacts(m, env) {
31015
31583
  try {
31016
31584
  if (!lstatSync3(file2.abs).isFile())
31017
31585
  continue;
31018
- before = readFileSync21(file2.abs, "utf-8");
31586
+ before = readFileSync22(file2.abs, "utf-8");
31019
31587
  } catch {
31020
31588
  continue;
31021
31589
  }
@@ -31250,7 +31818,7 @@ function canonical(stored) {
31250
31818
  function readSource(path, parse3) {
31251
31819
  let text;
31252
31820
  try {
31253
- text = readFileSync22(path, "utf-8");
31821
+ text = readFileSync23(path, "utf-8");
31254
31822
  } catch (e) {
31255
31823
  const code = e.code;
31256
31824
  return { names: [], broken: code !== "ENOENT" && code !== "ENOTDIR" };
@@ -31279,7 +31847,7 @@ function namesFromList(p) {
31279
31847
  }
31280
31848
  let lines2;
31281
31849
  try {
31282
- lines2 = readFileSync22(p.list, "utf-8").split("\n");
31850
+ lines2 = readFileSync23(p.list, "utf-8").split("\n");
31283
31851
  } catch {
31284
31852
  return null;
31285
31853
  }
@@ -31295,10 +31863,10 @@ function writeList(p, names) {
31295
31863
  const lines2 = [p.firstLine, ...comparable.length === names.length ? [] : [ACCOUNT_NAMES_ANY], ...comparable];
31296
31864
  const temp = `${p.list}.${String(process.pid)}.${randomBytes(4).toString("hex")}`;
31297
31865
  try {
31298
- mkdirSync9(join16(p.home, HALFCYCLE_DIR_NAME), { recursive: true, mode: 448 });
31299
- writeFileSync13(temp, `${lines2.join("\n")}
31866
+ mkdirSync10(join16(p.home, HALFCYCLE_DIR_NAME), { recursive: true, mode: 448 });
31867
+ writeFileSync14(temp, `${lines2.join("\n")}
31300
31868
  `, { mode: 384, flag: "wx" });
31301
- renameSync11(temp, p.list);
31869
+ renameSync12(temp, p.list);
31302
31870
  } catch {
31303
31871
  try {
31304
31872
  rmSync3(temp, { force: true });
@@ -31357,7 +31925,7 @@ function sumOf(texts, name) {
31357
31925
  }
31358
31926
  function textOf(path) {
31359
31927
  try {
31360
- return readFileSync22(path, "utf-8");
31928
+ return readFileSync23(path, "utf-8");
31361
31929
  } catch {
31362
31930
  return "";
31363
31931
  }
@@ -31980,7 +32548,7 @@ function isOneCall(call) {
31980
32548
 
31981
32549
  // dist/risky/allow-once.js
31982
32550
  import { createHash as createHash5 } from "node:crypto";
31983
- import { appendFileSync as appendFileSync5, readFileSync as readFileSync23, realpathSync as realpathSync8, renameSync as renameSync12, statSync as statSync9, unlinkSync as unlinkSync9, writeFileSync as writeFileSync14 } from "node:fs";
32551
+ import { appendFileSync as appendFileSync5, readFileSync as readFileSync24, realpathSync as realpathSync8, renameSync as renameSync13, statSync as statSync9, unlinkSync as unlinkSync9, writeFileSync as writeFileSync15 } from "node:fs";
31984
32552
  import { isAbsolute as isAbsolute6, relative as relative8, resolve as resolve8, sep as sep7 } from "node:path";
31985
32553
  var ALLOW_ONCE_MS = 5 * 60 * 1e3;
31986
32554
  var HELD_STEP_MAX_AGE_MS = 60 * 60 * 1e3;
@@ -32141,7 +32709,7 @@ function numberKept(path, key) {
32141
32709
  function readNumbers(path) {
32142
32710
  let text;
32143
32711
  try {
32144
- text = readFileSync23(path, "utf8");
32712
+ text = readFileSync24(path, "utf8");
32145
32713
  } catch (err) {
32146
32714
  return err.code === "ENOENT" ? { next: 1, steps: /* @__PURE__ */ new Map() } : null;
32147
32715
  }
@@ -32253,13 +32821,13 @@ function distinctSteps(held, numbersPath) {
32253
32821
  function claimHeld(path, session, now) {
32254
32822
  const claimed = `${path}.${process.pid}.${now}.claim`;
32255
32823
  try {
32256
- renameSync12(path, claimed);
32824
+ renameSync13(path, claimed);
32257
32825
  } catch {
32258
32826
  return [];
32259
32827
  }
32260
32828
  let text = "";
32261
32829
  try {
32262
- text = readFileSync23(claimed, "utf8");
32830
+ text = readFileSync24(claimed, "utf8");
32263
32831
  } catch {
32264
32832
  text = "";
32265
32833
  }
@@ -32314,7 +32882,7 @@ function nowOf(deps) {
32314
32882
  }
32315
32883
  function readRecord2(path, session) {
32316
32884
  try {
32317
- return parseRecord(readFileSync23(path, "utf8"), session);
32885
+ return parseRecord(readFileSync24(path, "utf8"), session);
32318
32886
  } catch {
32319
32887
  return null;
32320
32888
  }
@@ -32352,8 +32920,8 @@ function writeRecord(path, record2) {
32352
32920
  function writeWhole2(path, body) {
32353
32921
  const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
32354
32922
  try {
32355
- writeFileSync14(tmp, body, { mode: 384 });
32356
- renameSync12(tmp, path);
32923
+ writeFileSync15(tmp, body, { mode: 384 });
32924
+ renameSync13(tmp, path);
32357
32925
  return true;
32358
32926
  } catch {
32359
32927
  removeQuietly(tmp);
@@ -32453,7 +33021,7 @@ function decideAutonomous(facts, mayPublishNow) {
32453
33021
  }
32454
33022
 
32455
33023
  // dist/risky/own-copy.js
32456
- import { readFileSync as readFileSync24 } from "node:fs";
33024
+ import { readFileSync as readFileSync25 } from "node:fs";
32457
33025
  import { join as join17, posix as posix3, win32 as win322 } from "node:path";
32458
33026
  var OWN_COPY_START_TOOL = "Agent";
32459
33027
  var OWN_COPY_WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
@@ -32475,7 +33043,7 @@ function stringField2(o, key) {
32475
33043
  }
32476
33044
  function readOr(deps, path) {
32477
33045
  try {
32478
- return (deps.readFile ?? ((p) => readFileSync24(p, "utf-8")))(path);
33046
+ return (deps.readFile ?? ((p) => readFileSync25(p, "utf-8")))(path);
32479
33047
  } catch {
32480
33048
  return null;
32481
33049
  }
@@ -32621,7 +33189,7 @@ function judgeChange(input, call, deps) {
32621
33189
 
32622
33190
  // dist/risky/save.js
32623
33191
  import { randomBytes as randomBytes2 } from "node:crypto";
32624
- import { chmodSync as chmodSync2, constants as fsConstants, copyFileSync, lstatSync as lstatSync4, mkdirSync as mkdirSync10, readdirSync as readdirSync10, readFileSync as readFileSync25, readlinkSync, realpathSync as realpathSync9, renameSync as renameSync13, rmSync as rmSync4, statSync as statSync10, symlinkSync, writeFileSync as writeFileSync15 } from "node:fs";
33192
+ import { chmodSync as chmodSync2, constants as fsConstants, copyFileSync, lstatSync as lstatSync4, mkdirSync as mkdirSync11, readdirSync as readdirSync10, readFileSync as readFileSync26, readlinkSync, realpathSync as realpathSync9, renameSync as renameSync14, rmSync as rmSync4, statSync as statSync10, symlinkSync, writeFileSync as writeFileSync16 } from "node:fs";
32625
33193
  import { homedir as homedir12 } from "node:os";
32626
33194
  import { dirname as dirname7, isAbsolute as isAbsolute7, join as join18, resolve as resolve9 } from "node:path";
32627
33195
  var SAVE_LIMIT_BYTES = 2e8;
@@ -32667,7 +33235,7 @@ function saveFirst(staked, ctx) {
32667
33235
  const repository = repositoryOf(g);
32668
33236
  if (repository === null)
32669
33237
  return failed(saveDir);
32670
- writeFileSync15(join18(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
33238
+ writeFileSync16(join18(saveDir, SAVED_REPOSITORY_FILE_NAME), `${repository}
32671
33239
  `, { mode: 384 });
32672
33240
  const privateIndex = join18(saveDir, PRIVATE_INDEX_NAME);
32673
33241
  const commit = saveCommit(id, headCommit2, found2.tracked, plan.lines, privateIndex, g, now());
@@ -32693,9 +33261,9 @@ function saveFirst(staked, ctx) {
32693
33261
  if (!description.success)
32694
33262
  return failed(saveDir);
32695
33263
  const file2 = join18(saveDir, SAVED_FILE_NAME);
32696
- writeFileSync15(`${file2}.part`, `${JSON.stringify(description.data, null, 2)}
33264
+ writeFileSync16(`${file2}.part`, `${JSON.stringify(description.data, null, 2)}
32697
33265
  `, { mode: 384 });
32698
- renameSync13(`${file2}.part`, file2);
33266
+ renameSync14(`${file2}.part`, file2);
32699
33267
  ctx.beforeRef?.();
32700
33268
  const ref = g.run(["update-ref", "--no-deref", savedRefOf(id), commit, ""]);
32701
33269
  if (!ref.ok)
@@ -32815,7 +33383,7 @@ function copyOne(src, dst) {
32815
33383
  const st = lstatOrNull(src);
32816
33384
  if (st === null)
32817
33385
  return false;
32818
- mkdirSync10(dirname7(dst), { recursive: true, mode: 448 });
33386
+ mkdirSync11(dirname7(dst), { recursive: true, mode: 448 });
32819
33387
  if (st.isSymbolicLink()) {
32820
33388
  symlinkSync(readlinkSync(src), dst);
32821
33389
  return true;
@@ -32891,13 +33459,13 @@ var SaveGit = class {
32891
33459
  }
32892
33460
  };
32893
33461
  function freshId(folder2, newId) {
32894
- mkdirSync10(folder2, { recursive: true, mode: 448 });
33462
+ mkdirSync11(folder2, { recursive: true, mode: 448 });
32895
33463
  for (let i = 0; i < 5; i++) {
32896
33464
  const id = newId?.() ?? defaultId();
32897
33465
  if (!savedIdSchema.safeParse(id).success)
32898
33466
  return null;
32899
33467
  try {
32900
- mkdirSync10(join18(folder2, id), { mode: 448 });
33468
+ mkdirSync11(join18(folder2, id), { mode: 448 });
32901
33469
  return id;
32902
33470
  } catch (e) {
32903
33471
  if (e.code !== "EEXIST")
@@ -32922,7 +33490,7 @@ function repositoryOf(g) {
32922
33490
  }
32923
33491
  function markedRepository(dir) {
32924
33492
  try {
32925
- const text = readFileSync25(join18(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
33493
+ const text = readFileSync26(join18(dir, SAVED_REPOSITORY_FILE_NAME), "utf-8").trim();
32926
33494
  return text === "" ? null : text;
32927
33495
  } catch {
32928
33496
  return null;
@@ -32995,7 +33563,7 @@ function realOrSelf3(p) {
32995
33563
 
32996
33564
  // dist/autonomy-state.js
32997
33565
  import { spawnSync as spawnSync2 } from "node:child_process";
32998
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync11, readFileSync as readFileSync26, renameSync as renameSync14, rmSync as rmSync5, writeFileSync as writeFileSync16 } from "node:fs";
33566
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync12, readFileSync as readFileSync27, renameSync as renameSync15, rmSync as rmSync5, writeFileSync as writeFileSync17 } from "node:fs";
32999
33567
  import { homedir as homedir13, platform as platform2 } from "node:os";
33000
33568
  import { dirname as dirname8, join as join19 } from "node:path";
33001
33569
  var AUTONOMY_STATE_FILENAME = "autonomy.json";
@@ -33007,7 +33575,7 @@ function readCachedAutonomy(engagementId, nowMs, home) {
33007
33575
  if (!isEngagementId(engagementId))
33008
33576
  return null;
33009
33577
  try {
33010
- const parsed = autonomyStateFileSchema.safeParse(JSON.parse(readFileSync26(autonomyStateFilePath(engagementId, home), "utf-8")));
33578
+ const parsed = autonomyStateFileSchema.safeParse(JSON.parse(readFileSync27(autonomyStateFilePath(engagementId, home), "utf-8")));
33011
33579
  if (!parsed.success || !parsed.data.on)
33012
33580
  return null;
33013
33581
  const age = nowMs - Date.parse(parsed.data.readAt);
@@ -33056,12 +33624,12 @@ function writeAutonomyStateFile(engagementId, file2, home) {
33056
33624
  const target = autonomyStateFilePath(engagementId, home);
33057
33625
  const temp = join19(dirname8(target), `.${AUTONOMY_STATE_FILENAME}.${process.pid}.tmp`);
33058
33626
  try {
33059
- mkdirSync11(dirname8(target), { recursive: true, mode: 448 });
33060
- writeFileSync16(temp, `${JSON.stringify(file2)}
33627
+ mkdirSync12(dirname8(target), { recursive: true, mode: 448 });
33628
+ writeFileSync17(temp, `${JSON.stringify(file2)}
33061
33629
  `, { mode: 384 });
33062
33630
  if (platform2() !== "win32")
33063
33631
  chmodSync3(temp, 384);
33064
- renameSync14(temp, target);
33632
+ renameSync15(temp, target);
33065
33633
  return true;
33066
33634
  } catch {
33067
33635
  try {
@@ -33537,7 +34105,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
33537
34105
  const changeSet = buildChangeSet(fileDiffEntries, { ...context, excerpts: readExcerptConfig() });
33538
34106
  const configResult = readConfig();
33539
34107
  if (!configResult.config) {
33540
- await emitRunRecord({ runType: "hook", outcome: "unconfigured", changeSet, diffBase });
34108
+ await emitRunRecord({ runType: "hook", outcome: "unconfigured", changeSet, diffBase, repoFacts: context.repoFacts });
33541
34109
  if (!firstNoticeThisSession("credential-missing", repoRoot, sessionId))
33542
34110
  return;
33543
34111
  emitNeverConfigured(UNCONFIGURED_EXIT_STOP_FAMILY, readNeverConfiguredReason(), pinnedEngagementId(projectFolder()), reply);
@@ -33561,6 +34129,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
33561
34129
  outcome: outcomeForClientFailure(clientResult.kind),
33562
34130
  failureReason: failure.reason,
33563
34131
  changeSet,
34132
+ repoFacts: context.repoFacts,
33564
34133
  diffBase,
33565
34134
  credential: evaluation.token,
33566
34135
  credentialRefused: failure.kind === "credential"
@@ -33594,6 +34163,7 @@ async function evaluateSession(input, kind, reply, readSeat, profile) {
33594
34163
  phase: telemetryConfig.phase,
33595
34164
  outcome: "evaluated",
33596
34165
  changeSet,
34166
+ repoFacts: context.repoFacts,
33597
34167
  result: clientResult.envelope,
33598
34168
  diffBase
33599
34169
  });
@@ -33712,18 +34282,19 @@ function subagentStopped(input, deps = {}) {
33712
34282
  }
33713
34283
 
33714
34284
  // dist/hooks/stop.js
33715
- async function runStop(observeDeps = {}, timeDeps = {}, paceDeps = {}, profile = AGENT_PROFILES["claude-code"]) {
34285
+ async function runStop(observeDeps = {}, timeDeps = {}, paceDeps = {}, profile = AGENT_PROFILES["claude-code"], planDeps = {}) {
33716
34286
  const input = hookInputForRule(await readHookStdin(), profile.configFolder);
33717
34287
  recordTimeEvent("turn-end", input);
33718
34288
  await runSessionDiff(input, "stop", readSeatConfiguration, async (reply) => {
33719
34289
  await observeChanges(input.session_id, reply, observeDeps);
33720
34290
  await sendTimeRecord(timeDeps, { agent: profile.id });
33721
34291
  await paceAtTurnEnd(input, reply, { configFolder: profile.configFolder, ...paceDeps });
34292
+ await planUsageReport(input, { configFolder: profile.configFolder, ...planDeps });
33722
34293
  }, profile);
33723
34294
  }
33724
34295
 
33725
34296
  // dist/relayed-choices.js
33726
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync27, renameSync as renameSync15, rmSync as rmSync6, writeFileSync as writeFileSync17 } from "node:fs";
34297
+ import { mkdirSync as mkdirSync13, readFileSync as readFileSync28, renameSync as renameSync16, rmSync as rmSync6, writeFileSync as writeFileSync18 } from "node:fs";
33727
34298
  import { homedir as homedir15 } from "node:os";
33728
34299
  import { dirname as dirname9, join as join21 } from "node:path";
33729
34300
  function relayedChoicesFilePath(engagementId, home) {
@@ -33736,7 +34307,7 @@ function readRelayedChoices(engagementId, phase, home) {
33736
34307
  if (path === null)
33737
34308
  return null;
33738
34309
  try {
33739
- const parsed = relayedChoicesFileSchema.safeParse(JSON.parse(readFileSync27(path, "utf-8")));
34310
+ const parsed = relayedChoicesFileSchema.safeParse(JSON.parse(readFileSync28(path, "utf-8")));
33740
34311
  return parsed.success && parsed.data.phase === phase ? parsed.data : null;
33741
34312
  } catch {
33742
34313
  return null;
@@ -33767,10 +34338,10 @@ function recordRelayedChoice(opts) {
33767
34338
  return false;
33768
34339
  const temp = join21(dirname9(target), `.${RELAYED_CHOICES_FILENAME}.${process.pid}.tmp`);
33769
34340
  try {
33770
- mkdirSync12(dirname9(target), { recursive: true, mode: 448 });
33771
- writeFileSync17(temp, `${JSON.stringify(file2.data)}
34341
+ mkdirSync13(dirname9(target), { recursive: true, mode: 448 });
34342
+ writeFileSync18(temp, `${JSON.stringify(file2.data)}
33772
34343
  `, { mode: 384 });
33773
- renameSync15(temp, target);
34344
+ renameSync16(temp, target);
33774
34345
  return true;
33775
34346
  } catch {
33776
34347
  try {
@@ -33834,6 +34405,7 @@ var KEEP_GOING = /^keep going$/i;
33834
34405
  var STOP = /^stop(?: here)?$/i;
33835
34406
  var TIMES = /^limit it to ([0-9]{1,9}(?:\.[0-9]{1,2})?) times$/i;
33836
34407
  var FIGURE = /^limit it to ([0-9]{1,9}(?:\.[0-9]{1,2})?) (million|billion)(?: tokens)?$/i;
34408
+ var SHARE = /^limit it to ([0-9]{1,3})% of the week$/i;
33837
34409
  function limitPhraseShape(prompt) {
33838
34410
  if (typeof prompt !== "string")
33839
34411
  return null;
@@ -33852,6 +34424,11 @@ function limitPhraseShape(prompt) {
33852
34424
  const unit = figure[2].toLowerCase() === "billion" ? 1e9 : 1e6;
33853
34425
  return { kind: "figure", tokens: Math.round(Number(figure[1]) * unit) };
33854
34426
  }
34427
+ const share = SHARE.exec(line2);
34428
+ if (share !== null) {
34429
+ const percent = Number(share[1]);
34430
+ return percent >= 1 && percent <= 100 ? { kind: "share", percent } : null;
34431
+ }
33855
34432
  return null;
33856
34433
  }
33857
34434
 
@@ -33993,7 +34570,7 @@ function matchReviewPhraseShape(shape, reviews) {
33993
34570
  }
33994
34571
 
33995
34572
  // dist/hooks/shown-record.js
33996
- import { chmodSync as chmodSync4, readFileSync as readFileSync28, renameSync as renameSync16, unlinkSync as unlinkSync11, writeFileSync as writeFileSync18 } from "node:fs";
34573
+ import { chmodSync as chmodSync4, readFileSync as readFileSync29, renameSync as renameSync17, unlinkSync as unlinkSync11, writeFileSync as writeFileSync19 } from "node:fs";
33997
34574
  import { homedir as homedir16, platform as platform3 } from "node:os";
33998
34575
  import { join as join22 } from "node:path";
33999
34576
  function recordDir(engagementId, home) {
@@ -34006,7 +34583,7 @@ function readShownRecord(engagementId, home) {
34006
34583
  if (dir === null)
34007
34584
  return { entries: [], reviews: [] };
34008
34585
  try {
34009
- return parseShownRecord(readFileSync28(join22(dir, SHOWN_PLANS_FILENAME), "utf-8"));
34586
+ return parseShownRecord(readFileSync29(join22(dir, SHOWN_PLANS_FILENAME), "utf-8"));
34010
34587
  } catch {
34011
34588
  return { entries: [], reviews: [] };
34012
34589
  }
@@ -34028,7 +34605,7 @@ function moveListedReviewForward(engagementId, home, listed, reviewVersion, deci
34028
34605
  if (dir === null)
34029
34606
  return false;
34030
34607
  const path = join22(dir, SHOWN_PLANS_FILENAME);
34031
- const current = parseShownRecord(readFileSync28(path, "utf-8"));
34608
+ const current = parseShownRecord(readFileSync29(path, "utf-8"));
34032
34609
  const at = current.reviews.findIndex((r) => r.phase === listed.phase && r.shownAt === listed.shownAt && r.reviewVersion === listed.reviewVersion);
34033
34610
  if (at === -1)
34034
34611
  return false;
@@ -34036,10 +34613,10 @@ function moveListedReviewForward(engagementId, home, listed, reviewVersion, deci
34036
34613
  const reviews = current.reviews.map((review, i) => i === at ? { ...review, reviewVersion, parts: review.parts.map((p) => movedPart(p, acts.get(p.part))) } : review);
34037
34614
  const body = serialiseShownRecord({ entries: current.entries, reviews });
34038
34615
  temp = join22(dir, `.${SHOWN_PLANS_FILENAME}.${process.pid}.tmp`);
34039
- writeFileSync18(temp, body, { mode: 384 });
34616
+ writeFileSync19(temp, body, { mode: 384 });
34040
34617
  if (platform3() !== "win32")
34041
34618
  chmodSync4(temp, 384);
34042
- renameSync16(temp, path);
34619
+ renameSync17(temp, path);
34043
34620
  return true;
34044
34621
  } catch {
34045
34622
  if (temp !== null) {
@@ -34558,20 +35135,17 @@ function paceNoOwnerText() {
34558
35135
  function paceCannotReachText() {
34559
35136
  return `${PACE_NOTHING_CHANGED_OPENING}: Halfcycle could not be reached just now. Type the line again in a minute.`;
34560
35137
  }
34561
- function limitAlreadyUsedSuffix(limit, used) {
34562
- return limit <= used ? ` This phase has already used ${tokenCountWordsOf(used)}, so no new work will start until you raise it.` : "";
34563
- }
34564
- function limitKeepGoingDoneText(limit, used) {
34565
- return `Done. The limit is now ${tokenCountWordsOf(limit)} tokens. This phase has used ${tokenCountWordsOf(used)}. New work can start again.`;
35138
+ function limitKeepGoingDoneText(limit, used, weekCeilingTokens = null) {
35139
+ return limitKeepGoingShareText(limit, used, weekCeilingTokens);
34566
35140
  }
34567
35141
  function limitStopDoneText() {
34568
35142
  return 'Done. This phase is paused at its usage limit. Nothing is lost, and the agents already working will finish. Say "keep going" whenever you want to carry on.';
34569
35143
  }
34570
- function limitTimesDoneText(limit, typed, used) {
34571
- return `Done. The limit for this phase is now ${tokenCountWordsOf(limit)} tokens. That is ${typed} times what it is expected to use.` + limitAlreadyUsedSuffix(limit, used);
35144
+ function limitTimesDoneText(limit, typed, used, weekCeilingTokens = null) {
35145
+ return limitTimesShareText(limit, typed, used, weekCeilingTokens);
34572
35146
  }
34573
- function limitFigureDoneText(limit, used) {
34574
- return `Done. The limit for this phase is now ${tokenCountWordsOf(limit)} tokens.${limitAlreadyUsedSuffix(limit, used)}`;
35147
+ function limitFigureDoneText(limit, used, weekCeilingTokens = null) {
35148
+ return limitFigureShareText(limit, used, weekCeilingTokens);
34575
35149
  }
34576
35150
  function limitNotReachedText() {
34577
35151
  return `${PACE_NOTHING_CHANGED_OPENING}: this phase has not reached its usage limit, so there is nothing to answer.`;
@@ -34579,11 +35153,14 @@ function limitNotReachedText() {
34579
35153
  function limitNoBuildText() {
34580
35154
  return `${PACE_NOTHING_CHANGED_OPENING}: no build is running in this project yet.`;
34581
35155
  }
34582
- function limitTimesRangeText() {
34583
- return `${PACE_NOTHING_CHANGED_OPENING}: a limit is between ${String(LIMIT_MULTIPLE_RANGE.min)} and ${String(LIMIT_MULTIPLE_RANGE.max)} times what is expected. Say a number in that range, like "limit it to 1.5 times".`;
35156
+ function limitTimesRangeText(weekCeilingTokens = null) {
35157
+ return limitTimesRangeShareText(LIMIT_MULTIPLE_RANGE, weekCeilingTokens);
35158
+ }
35159
+ function limitNoExpectedText(weekCeilingTokens = null) {
35160
+ return limitNoExpectedShareText(weekCeilingTokens);
34584
35161
  }
34585
- function limitNoExpectedText() {
34586
- return `${PACE_NOTHING_CHANGED_OPENING}: there is no expected usage yet, so say a figure, like "limit it to 300 million".`;
35162
+ function limitShareNotSetText() {
35163
+ return LIMIT_SHARE_NOT_SET_TEXT;
34587
35164
  }
34588
35165
  function limitFigureRangeText() {
34589
35166
  return `${PACE_NOTHING_CHANGED_OPENING}: a limit is between ${tokenCountWordsOf(LIMIT_FIGURE_RANGE.min)} and ${tokenCountWordsOf(LIMIT_FIGURE_RANGE.max)} tokens.`;
@@ -34860,8 +35437,11 @@ function decideLimit(shape, opts) {
34860
35437
  const file2 = dir === null ? null : openPhaseLimit(dir, readPaceStateFile(dir).basis);
34861
35438
  if (dir === null || file2 === null)
34862
35439
  return say(limitNoBuildText());
35440
+ const ceiling = weeklyCeilingHere(opts, agent);
35441
+ if (shape.kind === "share" && ceiling === null)
35442
+ return say(limitShareNotSetText());
34863
35443
  if (shape.kind === "times" && !multipleInRange(shape.multiple))
34864
- return say(limitTimesRangeText());
35444
+ return say(limitTimesRangeText(ceiling));
34865
35445
  if (shape.kind === "figure" && !figureInRange(shape.tokens))
34866
35446
  return say(limitFigureRangeText());
34867
35447
  const used = readPhaseUsage(dir, file2.phase);
@@ -34885,11 +35465,11 @@ function decideLimit(shape, opts) {
34885
35465
  return say(limitNotSavedText());
34886
35466
  if (typed === "cannot-tell")
34887
35467
  relay();
34888
- return say(limitKeepGoingDoneText(next2.limit, used));
35468
+ return say(limitKeepGoingDoneText(next2.limit, used, ceiling));
34889
35469
  }
34890
35470
  if (shape.kind === "times" && !(file2.expected > 0))
34891
- return say(limitNoExpectedText());
34892
- const limit = shape.kind === "times" ? limitOf(file2.expected, shape.multiple) : shape.tokens;
35471
+ return say(limitNoExpectedText(ceiling));
35472
+ const limit = shape.kind === "times" ? limitOf(file2.expected, shape.multiple) : shape.kind === "share" ? Math.max(1, Math.round(shape.percent / 100 * (ceiling ?? 0))) : shape.tokens;
34893
35473
  const raise = limit > file2.limit;
34894
35474
  if (raise && typed === "not-attended")
34895
35475
  return say(limitNotAttendedText());
@@ -34898,7 +35478,32 @@ function decideLimit(shape, opts) {
34898
35478
  return say(limitNotSavedText());
34899
35479
  if (raise && typed === "cannot-tell")
34900
35480
  relay();
34901
- return say(shape.kind === "times" ? limitTimesDoneText(limit, shape.typed, used) : limitFigureDoneText(limit, used));
35481
+ return say(shape.kind === "times" ? limitTimesDoneText(limit, shape.typed, used, ceiling) : limitFigureDoneText(limit, used, ceiling));
35482
+ }
35483
+ function weeklyCeilingHere(opts, agent) {
35484
+ try {
35485
+ const said = promptRecordPath(opts.input);
35486
+ const key = configFolderKey({
35487
+ env: opts.env ?? process.env,
35488
+ transcriptPath: said,
35489
+ home: opts.home,
35490
+ ...agent === "codex" ? { rule: "codex-home" } : {}
35491
+ });
35492
+ return weeklyCeilingOf(key, { home: opts.home });
35493
+ } catch {
35494
+ return null;
35495
+ }
35496
+ }
35497
+ function promptRecordPath(input) {
35498
+ try {
35499
+ const parsed = JSON.parse(input);
35500
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
35501
+ return null;
35502
+ const said = parsed["transcript_path"];
35503
+ return typeof said === "string" && said !== "" ? said : null;
35504
+ } catch {
35505
+ return null;
35506
+ }
34902
35507
  }
34903
35508
  function adjustPaceState(dir, opts, number4) {
34904
35509
  try {
@@ -35221,7 +35826,7 @@ function runUserPromptFindings(opts) {
35221
35826
  }
35222
35827
 
35223
35828
  // dist/resolve-engagement.js
35224
- import { existsSync as existsSync9, readFileSync as readFileSync29, realpathSync as realpathSync10 } from "node:fs";
35829
+ import { existsSync as existsSync9, readFileSync as readFileSync30, realpathSync as realpathSync10 } from "node:fs";
35225
35830
  import { join as join23, sep as sep8 } from "node:path";
35226
35831
  import { homedir as homedir17 } from "node:os";
35227
35832
  function defaultRegistryPath() {
@@ -35231,7 +35836,7 @@ function readRegistry(registryPath = defaultRegistryPath()) {
35231
35836
  if (!existsSync9(registryPath))
35232
35837
  return {};
35233
35838
  try {
35234
- const parsed = JSON.parse(readFileSync29(registryPath, "utf-8"));
35839
+ const parsed = JSON.parse(readFileSync30(registryPath, "utf-8"));
35235
35840
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
35236
35841
  return {};
35237
35842
  const out = {};
@@ -35279,12 +35884,12 @@ function runResolveEngagement(cwd, registryPath) {
35279
35884
  }
35280
35885
 
35281
35886
  // dist/refresh-credential.js
35282
- import { lstatSync as lstatSync5, readFileSync as readFileSync31, realpathSync as realpathSync11 } from "node:fs";
35887
+ import { lstatSync as lstatSync5, readFileSync as readFileSync32, realpathSync as realpathSync11 } from "node:fs";
35283
35888
  import { homedir as homedir19 } from "node:os";
35284
35889
  import { join as join24, resolve as resolve12 } from "node:path";
35285
35890
 
35286
35891
  // dist/mcp-headers.js
35287
- import { readFileSync as readFileSync30 } from "node:fs";
35892
+ import { readFileSync as readFileSync31 } from "node:fs";
35288
35893
  import { homedir as homedir18 } from "node:os";
35289
35894
  import { dirname as dirname10, resolve as resolve11 } from "node:path";
35290
35895
  var URL_FLAG = "--url";
@@ -35318,7 +35923,7 @@ function projectRootFromBinary(binaryPath) {
35318
35923
  function readPinnedVersion(root) {
35319
35924
  let raw;
35320
35925
  try {
35321
- raw = readFileSync30(resolve11(root, BUNDLE_PIN_REL), "utf-8");
35926
+ raw = readFileSync31(resolve11(root, BUNDLE_PIN_REL), "utf-8");
35322
35927
  } catch {
35323
35928
  return void 0;
35324
35929
  }
@@ -35580,7 +36185,7 @@ var SESSION_START_PROBE_ATTEMPTS = 2;
35580
36185
  function registeredMcpUrl(projectRoot) {
35581
36186
  let parsed;
35582
36187
  try {
35583
- parsed = JSON.parse(readFileSync31(resolve12(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
36188
+ parsed = JSON.parse(readFileSync32(resolve12(projectRoot, MCP_REGISTRATION_REL), "utf-8"));
35584
36189
  } catch {
35585
36190
  return void 0;
35586
36191
  }
@@ -35769,7 +36374,7 @@ function headlessInUntrustedFolder(input, profile, opts) {
35769
36374
  const cwd = input["cwd"];
35770
36375
  const folder2 = typeof cwd === "string" && cwd !== "" ? cwd : projectRootFromBinary(opts.binaryPath);
35771
36376
  const stateFile = join24(readClaudeConfigFolder() ?? opts.home ?? homedir19(), CLAUDE_STATE_FILENAME);
35772
- return trustOfWorkingFolder(folder2, stateFile, process.platform, { lstatSync: lstatSync5, readFileSync: readFileSync31, realpathSync: realpathSync11 }) === "not-trusted";
36377
+ return trustOfWorkingFolder(folder2, stateFile, process.platform, { lstatSync: lstatSync5, readFileSync: readFileSync32, realpathSync: realpathSync11 }) === "not-trusted";
35773
36378
  }
35774
36379
  async function runRefreshCredential(opts) {
35775
36380
  const writeErr = opts.writeErr ?? defaultWriteErr2;
@@ -35862,7 +36467,7 @@ async function runRefreshCredential(opts) {
35862
36467
  import { fileURLToPath } from "node:url";
35863
36468
 
35864
36469
  // dist/capture.js
35865
- import { readFileSync as readFileSync32, writeFileSync as writeFileSync19, mkdirSync as mkdirSync13, existsSync as existsSync10 } from "node:fs";
36470
+ import { readFileSync as readFileSync33, writeFileSync as writeFileSync20, mkdirSync as mkdirSync14, existsSync as existsSync10 } from "node:fs";
35866
36471
  import { dirname as dirname11 } from "node:path";
35867
36472
  function runCapture(provider, endpoint, opts = {}) {
35868
36473
  const repoRoot = opts.repoRoot ?? process.cwd();
@@ -35898,8 +36503,8 @@ function runCapture(provider, endpoint, opts = {}) {
35898
36503
  const fixture = fixturePath(repoRoot, provider, slug);
35899
36504
  const manifestFile = manifestPath(repoRoot);
35900
36505
  try {
35901
- mkdirSync13(dirname11(fixture), { recursive: true });
35902
- writeFileSync19(fixture, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
36506
+ mkdirSync14(dirname11(fixture), { recursive: true });
36507
+ writeFileSync20(fixture, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
35903
36508
  } catch (err) {
35904
36509
  const msg = err instanceof Error ? err.message : String(err);
35905
36510
  return { ok: false, kind: "write-failed", message: `could not write fixture: ${msg}` };
@@ -35930,7 +36535,7 @@ function readInputFile(fromFile) {
35930
36535
  if (!fromFile)
35931
36536
  return void 0;
35932
36537
  try {
35933
- return readFileSync32(fromFile, "utf-8");
36538
+ return readFileSync33(fromFile, "utf-8");
35934
36539
  } catch {
35935
36540
  return void 0;
35936
36541
  }
@@ -35943,7 +36548,7 @@ function relativeFixturePath(repoRoot, fixture) {
35943
36548
  // dist/prove.js
35944
36549
  import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
35945
36550
  import { createHash as createHash6, randomUUID as randomUUID3 } from "node:crypto";
35946
- import { createWriteStream, mkdirSync as mkdirSync14, readdirSync as readdirSync11, readFileSync as readFileSync33, renameSync as renameSync17, unlinkSync as unlinkSync12, writeFileSync as writeFileSync20 } from "node:fs";
36551
+ import { createWriteStream, mkdirSync as mkdirSync15, readdirSync as readdirSync11, readFileSync as readFileSync34, renameSync as renameSync18, unlinkSync as unlinkSync12, writeFileSync as writeFileSync21 } from "node:fs";
35947
36552
  import { homedir as homedir20 } from "node:os";
35948
36553
  import { dirname as dirname12, join as join25 } from "node:path";
35949
36554
 
@@ -36140,10 +36745,10 @@ function parseProveArgs(args2) {
36140
36745
  };
36141
36746
  }
36142
36747
  function writeJsonFile2(path, value) {
36143
- mkdirSync14(dirname12(path), { recursive: true, mode: 448 });
36748
+ mkdirSync15(dirname12(path), { recursive: true, mode: 448 });
36144
36749
  const tmp = `${path}.${process.pid}.${randomUUID3()}.tmp`;
36145
- writeFileSync20(tmp, JSON.stringify(value), { mode: 384 });
36146
- renameSync17(tmp, path);
36750
+ writeFileSync21(tmp, JSON.stringify(value), { mode: 384 });
36751
+ renameSync18(tmp, path);
36147
36752
  }
36148
36753
  function subjectKey(subject) {
36149
36754
  return subject.kind === "task" ? ["task", subject.phase, subject.task] : ["change", subject.change];
@@ -36154,7 +36759,7 @@ function keptCheckPath(dir, subject, label) {
36154
36759
  }
36155
36760
  function readKeptCheck(dir, subject, label) {
36156
36761
  try {
36157
- const raw = JSON.parse(readFileSync33(keptCheckPath(dir, subject, label), "utf-8"));
36762
+ const raw = JSON.parse(readFileSync34(keptCheckPath(dir, subject, label), "utf-8"));
36158
36763
  if (typeof raw.command !== "string" || raw.command === "" || typeof raw.commandId !== "string")
36159
36764
  return null;
36160
36765
  const b = raw.before;
@@ -36179,7 +36784,7 @@ function readQueue(dir) {
36179
36784
  const path = join25(dir, QUEUE_DIR_NAME, name);
36180
36785
  let parsed = null;
36181
36786
  try {
36182
- parsed = testRunReportSchema.safeParse(JSON.parse(readFileSync33(path, "utf-8")));
36787
+ parsed = testRunReportSchema.safeParse(JSON.parse(readFileSync34(path, "utf-8")));
36183
36788
  } catch {
36184
36789
  parsed = null;
36185
36790
  }
@@ -36256,7 +36861,7 @@ function runCommand(command, repoRoot, logPath2, env, deps) {
36256
36861
  const deadline = deps.deadlineMs ?? PROVE_RUN_DEADLINE_MS;
36257
36862
  const childEnv = { ...env };
36258
36863
  delete childEnv[SERVICE_TOKEN_VAR];
36259
- mkdirSync14(dirname12(logPath2), { recursive: true, mode: 448 });
36864
+ mkdirSync15(dirname12(logPath2), { recursive: true, mode: 448 });
36260
36865
  const log = createWriteStream(logPath2, { mode: 384 });
36261
36866
  const began = process.hrtime.bigint();
36262
36867
  return new Promise((resolve13) => {
@@ -36422,7 +37027,7 @@ ${PROVE_USAGE}`);
36422
37027
  const end = await runCommand(command, repoRoot, logPath2, env, deps);
36423
37028
  let output = "";
36424
37029
  try {
36425
- output = readFileSync33(logPath2, "utf-8");
37030
+ output = readFileSync34(logPath2, "utf-8");
36426
37031
  } catch {
36427
37032
  output = "";
36428
37033
  }
@@ -36472,8 +37077,8 @@ ${PROVE_USAGE}`);
36472
37077
 
36473
37078
  // dist/statusline.js
36474
37079
  import { spawn as spawn3 } from "node:child_process";
36475
- import { readFileSync as readFileSync34 } from "node:fs";
36476
- import nodePath6 from "node:path";
37080
+ import { readFileSync as readFileSync35 } from "node:fs";
37081
+ import nodePath7 from "node:path";
36477
37082
  var OWN_STATUS_LINE_DEADLINE_MS = 5e3;
36478
37083
  var PERSON_SETTINGS_FILENAME = "settings.json";
36479
37084
  var OWN_COMMAND_MARK = /guard-runner\.sh["']?\s+statusline\b/;
@@ -36510,7 +37115,7 @@ function halfcycleStatusLineOf(figures) {
36510
37115
  }
36511
37116
  function personStatusLineCommand(configFolder) {
36512
37117
  try {
36513
- const v = JSON.parse(readFileSync34(nodePath6.join(configFolder, PERSON_SETTINGS_FILENAME), "utf-8"));
37118
+ const v = JSON.parse(readFileSync35(nodePath7.join(configFolder, PERSON_SETTINGS_FILENAME), "utf-8"));
36514
37119
  const line2 = v["statusLine"];
36515
37120
  if (typeof line2 !== "object" || line2 === null)
36516
37121
  return null;