frizz 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/frizz.js +126 -25
  2. package/package.json +3 -3
package/dist/frizz.js CHANGED
@@ -11775,7 +11775,7 @@ var init_interactions = __esm({
11775
11775
  InteractionFieldId = external_exports.string().min(1).max(128).regex(/^[A-Za-z_][A-Za-z0-9_.:-]*$/).refine((value) => !["__proto__", "constructor", "prototype"].includes(value), "reserved field id");
11776
11776
  InteractionDecisionId = external_exports.string().min(1).max(80).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
11777
11777
  InteractionProvider = external_exports.object({
11778
- kind: external_exports.enum(["claude", "codex", "frizz"]),
11778
+ kind: external_exports.enum(["claude", "codex", "acp", "frizz"]),
11779
11779
  name: InteractionLabel.optional(),
11780
11780
  version: InteractionOpaqueId.optional()
11781
11781
  }).strict();
@@ -12466,10 +12466,15 @@ var init_shell_writes = __esm({
12466
12466
  });
12467
12467
 
12468
12468
  // packages/shared/src/index.ts
12469
+ function requireEffortOutsideAcp(profile, ctx) {
12470
+ if (profile.backend !== "acp" && profile.effort === void 0) {
12471
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["effort"], message: `effort is required for a ${profile.backend} profile` });
12472
+ }
12473
+ }
12469
12474
  function fallbackPort(base) {
12470
12475
  return base + 1e4;
12471
12476
  }
12472
- var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, AskQuestionKind, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, HUMAN_GAP_FLOOR_MS, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
12477
+ var import_yaml, ATTACHMENT_IMAGE_EXTENSIONS, ATTACHMENT_TEXT_EXTENSIONS, ATTACHMENT_OFFICE_EXTENSIONS, ATTACHMENT_DATA_EXTENSIONS, ATTACHMENT_ARCHIVE_EXTENSIONS, ATTACHMENT_DOC_EXTENSIONS, ATTACHMENT_EXTENSIONS, ATTACHMENT_MAX_BASE64_CHARS, ATTACHMENT_MAX_BYTES, ATTACHMENT_EXT_SET, ATTACHMENT_ACCEPT, FrizzStatus, BlockMechanism, RuntimeState, Backend, CodexModel, AcpAgent, AcpAgentModel, AcpAgentModels, AcpAgentModelsInput, ThreadProfileOption, ThreadAgent, SubAgentView, BgShellView, CompletionHoldOp, CompletionHold, AskOption, AskQuestion, PendingAsk, AwaitingHint, RETIRED_AWAITING_KINDS, RETIRED_LINE_RE, AWAITING_FOR_MAX_MS, SnoozeUntil, SNOOZE_PROMPT_MAX, SnoozePrompt, RECURRING_PROMPT_MAX, RECURRING_MIN_INTERVAL_SECONDS, RECURRING_MAX_INTERVAL_SECONDS, RecurringPromptText, RecurringIntervalSeconds, ThreadRecurringPrompt, TIMER_PROMPT_MAX, TIMER_MAX_DELAY_SECONDS, TimerPromptText, SIGNOFF_NUDGE_MARKER, SIGNOFF_NUDGE_MESSAGE, ThreadWatchKind, GithubChecksState, GithubMergeState, GithubWatchStatus, GithubIssueStatus, ThreadWatchView, PR_WATCH_FOR_MAX_MS, PR_WATCH_DEFAULT_FOR_MS, PrWatchView, AddOwnPrWatchInput, AddOwnPrWatchResult, DropOwnPrWatchInput, DropOwnPrWatchResult, OwnWatchKind, AddOwnWatchInput, OwnWatchView, AddOwnWatchResult, DropOwnWatchInput, DropOwnWatchResult, AskQuestionKind, AskedOptionSchema, AskedQuestionSchema, AskInput, RegisteredQuestionView, AskResult, UnaskInput, UnaskResult, QuestionAnswerSchema, AnswerQuestionsInput, AnswerQuestionsResult, DismissQuestionsInput, DismissQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, AccountLoginStartInput, AccountLoginStartResult, AccountLoginStatusInput, AccountLoginStatusResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, SetThreadSnoozeInput, SetThreadPinnedInput, SetThreadRecurringPromptInput, SetOwnThreadRecurringPromptInput, SetOwnThreadRecurringPromptResult, GetOwnThreadRecurringPromptInput, OwnThreadRecurringPromptResult, SetOwnThreadTimerInput, CancelOwnThreadTimerInput, ListOwnThreadTimersInput, OwnThreadTimersResult, ThreadActivityItem, ListOwnThreadActivityInput, OwnThreadActivityResult, SetOwnThreadTimerResult, CancelOwnThreadTimerResult, SetOwnThreadStopHookInput, SetOwnThreadHeartbeatInput, ThreadPluginReloadResult, RenameThreadInput, AiRenameThreadInput, AiRenameThreadResult, SetOwnThreadTitleInput, SetOwnThreadTitleResult, SetThreadPermissionInput, SetThreadPermissionResult, ThreadProfileOptionsInput, ThreadProfileOptionsResult, ThreadSkillSource, ThreadSkill, ThreadSkillsInput, ThreadSkillsResult, SetThreadProfileInput, SetThreadProfileResult, DISPATCH_TASK_BANNER, DISPATCH_TASK_BANNER_MARKER, HUMAN_GAP_FLOOR_MS, GithubWakeItem, GithubWakeSteer, WAKE_SCOPE, WAKE_REF, WAKE_SINGLE, WAKE_MULTI_HEAD, PR_WATCH_FINISHED, PR_WATCH_CI_PASSED, PR_WATCH_CI_GATED, PR_WATCH_STATE, PR_WATCH_CI_FAILED, GithubStatus, GithubRefCard, GithubRefPreviewInput, GithubRefPreviewResult, GithubItem, GithubListInput, GithubListResult, GithubBatchInput, GithubBatchResult, BoardMeta, ServerEvent, TranscriptEdit, TranscriptTodo, TranscriptToolCall, TranscriptPart, TranscriptMessage, TranscriptPageCursor, EditedFile, TranscriptPage, TranscriptEarlierInput, SocketTranscriptSlug, SocketFilePath, SocketTranscriptClientMsg, SocketFileClientMsg, SocketClientMsg, ProjectCard, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
12473
12478
  var init_src = __esm({
12474
12479
  "packages/shared/src/index.ts"() {
12475
12480
  "use strict";
@@ -12588,7 +12593,7 @@ var init_src = __esm({
12588
12593
  "exited"
12589
12594
  // the worker process that owned this session is gone, or is no longer driving its turn
12590
12595
  ]);
12591
- Backend = external_exports.enum(["claude", "codex"]);
12596
+ Backend = external_exports.enum(["claude", "codex", "acp"]);
12592
12597
  CodexModel = external_exports.object({
12593
12598
  slug: external_exports.string(),
12594
12599
  displayName: external_exports.string(),
@@ -12602,6 +12607,23 @@ var init_src = __esm({
12602
12607
  contextWindow: external_exports.number().int().positive().optional(),
12603
12608
  maxContextWindow: external_exports.number().int().positive().optional()
12604
12609
  });
12610
+ AcpAgent = external_exports.object({
12611
+ id: external_exports.string(),
12612
+ label: external_exports.string(),
12613
+ command: external_exports.string(),
12614
+ available: external_exports.boolean()
12615
+ });
12616
+ AcpAgentModel = external_exports.object({ id: external_exports.string(), name: external_exports.string() });
12617
+ AcpAgentModels = external_exports.object({
12618
+ agentId: external_exports.string(),
12619
+ models: external_exports.array(AcpAgentModel),
12620
+ /** The model the agent opens a session on when Frizz asks for none. */
12621
+ current: external_exports.string().optional(),
12622
+ /** Why the list is empty, when the probe could not open a session (not installed, not logged in…). */
12623
+ error: external_exports.string().optional(),
12624
+ probedAt: external_exports.string()
12625
+ });
12626
+ AcpAgentModelsInput = external_exports.object({ agentId: external_exports.string().min(1).max(100), refresh: external_exports.boolean().optional() }).strict();
12605
12627
  ThreadProfileOption = external_exports.object({
12606
12628
  model: external_exports.string().min(1),
12607
12629
  label: external_exports.string().min(1),
@@ -12779,7 +12801,7 @@ var init_src = __esm({
12779
12801
  questions: external_exports.array(AskQuestion)
12780
12802
  });
12781
12803
  AwaitingHint = external_exports.object({
12782
- kind: external_exports.enum(["shell", "agent", "timer", "pr", "for", "title"]),
12804
+ kind: external_exports.enum(["shell", "agent", "timer", "pr", "issue", "for", "title"]),
12783
12805
  value: external_exports.string()
12784
12806
  });
12785
12807
  RETIRED_AWAITING_KINDS = ["watch", "pr-watch", "human", "ci", "session", "shell", "agent", "timer", "pr", "reason"];
@@ -12929,6 +12951,20 @@ var init_src = __esm({
12929
12951
  * must hear about. Empty when nothing is failing. */
12930
12952
  failureSig: external_exports.string().optional()
12931
12953
  }).strict();
12954
+ GithubIssueStatus = external_exports.object({
12955
+ /** OPEN | CLOSED, lowercased. A closed issue ends the wait outright. */
12956
+ state: external_exports.enum(["open", "closed"]),
12957
+ /** GitHub's own reason for a close — `completed`, `not_planned`, `duplicate` — lowercased, when it
12958
+ * gave one. Absent while open. */
12959
+ stateReason: external_exports.string().optional(),
12960
+ /** The issue's title, as of the last poll — the one fact a human needs to place a bare `owner/repo#N`
12961
+ * on the card. Capped, because a title is a headline and the card is not the place to read one. */
12962
+ title: external_exports.string().max(200).optional(),
12963
+ /** The conversation's size, so the card can say "14 comments" the way GitHub's list does. */
12964
+ comments: external_exports.number().int().nonnegative(),
12965
+ /** When frizz last heard from GitHub, for the same reason `GithubWatchStatus.polledAt` carries it. */
12966
+ polledAt: external_exports.string()
12967
+ }).strict();
12932
12968
  ThreadWatchView = external_exports.object({
12933
12969
  id: external_exports.string(),
12934
12970
  kind: ThreadWatchKind,
@@ -12937,6 +12973,15 @@ var init_src = __esm({
12937
12973
  createdAt: external_exports.string(),
12938
12974
  /** `github` rows only, and absent until the first successful poll. */
12939
12975
  github: GithubWatchStatus.optional(),
12976
+ /** `github` rows only: WHAT the row watches. A pull request and an issue share the kind — they are one
12977
+ * registry, one ref grammar (`owner/repo#N`) and one place on every surface that lists waits — and
12978
+ * differ in what the poll can read off them, which is what this field lets a card branch on.
12979
+ * Optional, and absent means a pull request — a row from a server that predates issues reads as the
12980
+ * PR it always was, and the non-github kinds never carry it. */
12981
+ subject: external_exports.enum(["pull", "issue"]).optional(),
12982
+ /** `github` rows watching an ISSUE only, and absent until the first successful poll — the twin of the
12983
+ * `github` half above, for the rows whose subject has no checks to report. */
12984
+ issue: GithubIssueStatus.optional(),
12940
12985
  /** `timer` rows only: the armed timer's own registration, which is everything the row renders — the
12941
12986
  * worker's prompt is the row's NAME (a `tmr_…` id names nothing to a human) and the fire instant is
12942
12987
  * its status. Unlike `github` there is no polled half to be absent: a timer that exists is fully
@@ -12949,10 +12994,15 @@ var init_src = __esm({
12949
12994
  id: external_exports.string(),
12950
12995
  /** `owner/repo#N`, normalized — the same string the board's row and the status book are keyed by. */
12951
12996
  target: external_exports.string(),
12997
+ /** A pull request or an issue. Both live in one registry (`pr_watch`) and answer to one drop verb;
12998
+ * `watch_pr` and `watch_issue` each list only their own. Defaulted for a row an older server sends. */
12999
+ kind: external_exports.enum(["pull", "issue"]).default("pull"),
12952
13000
  state: external_exports.enum(["armed", "dropped", "settled"]),
12953
13001
  createdAt: external_exports.string(),
12954
13002
  /** The PR's checks/mergeability as the poller last saw it. Absent until the first successful poll. */
12955
- github: GithubWatchStatus.optional()
13003
+ github: GithubWatchStatus.optional(),
13004
+ /** An ISSUE's state as the poller last saw it — `kind: "issue"` rows only, absent until polled. */
13005
+ issue: GithubIssueStatus.optional()
12956
13006
  }).strict();
12957
13007
  AddOwnPrWatchInput = external_exports.object({
12958
13008
  slug: ThreadSlug,
@@ -12977,7 +13027,13 @@ var init_src = __esm({
12977
13027
  * already running, with no recourse from inside those threads. Absent ⇒ PR_WATCH_DEFAULT_FOR, which
12978
13028
  * still BOUNDS the poll; the point of the field is that a worker chooses, and one that cannot choose
12979
13029
  * is better bounded than broken. */
12980
- for: external_exports.string().trim().min(1).max(16).optional()
13030
+ for: external_exports.string().trim().min(1).max(16).optional(),
13031
+ /** What `target` names. `issue` (from `mcp__frizz__watch_issue`, 2026-09-14) registers a watcher on a
13032
+ * GitHub issue — same registry, same poll cadence, same drop verb, but a different probe (`gh issue
13033
+ * view`), a different GraphQL fragment and a report with no CI in it. ABSENT MEANS A PULL REQUEST,
13034
+ * for the same reason `for` is optional on the wire: every `watch_pr` binary dispatched before this
13035
+ * field existed sends nothing here and must go on meaning what it always did. */
13036
+ kind: external_exports.enum(["pull", "issue"]).optional()
12981
13037
  }).strict();
12982
13038
  AddOwnPrWatchResult = external_exports.object({
12983
13039
  id: external_exports.string(),
@@ -13525,14 +13581,15 @@ var init_src = __esm({
13525
13581
  codex: ProviderAuth,
13526
13582
  emails: AccountEmails
13527
13583
  });
13528
- AccountLogoutInput = external_exports.object({ backend: external_exports.enum(["claude", "codex"]) }).strict();
13584
+ AccountBackend = external_exports.enum(["claude", "codex"]);
13585
+ AccountLogoutInput = external_exports.object({ backend: AccountBackend }).strict();
13529
13586
  AccountLogoutResult = external_exports.object({
13530
13587
  status: external_exports.enum(["done", "blocked", "failed"]),
13531
13588
  auth: ProviderAuth,
13532
13589
  activeThreads: external_exports.number().int().positive().optional(),
13533
13590
  detail: external_exports.string().max(200).optional()
13534
13591
  });
13535
- AccountLoginStartInput = external_exports.object({ backend: external_exports.enum(["claude", "codex"]) }).strict();
13592
+ AccountLoginStartInput = external_exports.object({ backend: AccountBackend }).strict();
13536
13593
  AccountLoginStartResult = external_exports.object({ attemptId: ThreadSlug });
13537
13594
  AccountLoginStatusInput = external_exports.object({ attemptId: ThreadSlug }).strict();
13538
13595
  AccountLoginStatusResult = external_exports.object({
@@ -13607,6 +13664,15 @@ var init_src = __esm({
13607
13664
  // it (codex compacts at 90% of the resolved window). Unset ⇒ nothing is sent and the model's stock
13608
13665
  // window applies. Optional so an old blob parses; defaultSettings leaves it unset.
13609
13666
  codexContextWindow: external_exports.number().int().positive().optional(),
13667
+ // The operator's OWN Agent Client Protocol agents, merged over the built-in catalogue by id
13668
+ // (server/backend/acp-agents.ts). `command` is an executable name on PATH or an absolute path;
13669
+ // `args` is what puts it into ACP mode (`["acp"]`, `["--acp"]`). Optional so an old blob parses.
13670
+ acpAgents: external_exports.array(external_exports.object({
13671
+ id: external_exports.string().trim().min(1).max(64).regex(/^[a-z0-9][a-z0-9._-]*$/i),
13672
+ label: external_exports.string().trim().min(1).max(80),
13673
+ command: external_exports.string().trim().min(1).max(1024),
13674
+ args: external_exports.array(external_exports.string().max(1024)).max(32).optional()
13675
+ })).max(32).optional(),
13610
13676
  // The GitHub batch-dispatch prompt template (the picker's per-item worker prompt). Optional: when
13611
13677
  // unset OR blank the server falls back to its exported DEFAULT_GITHUB_PROMPT. Substitution tokens
13612
13678
  // the server fills: {repo} {n} {title} {url} {labels} {body}. The leading `THREAD: <slug>` tag is
@@ -13633,28 +13699,38 @@ var init_src = __esm({
13633
13699
  DispatchPreferences = external_exports.object({
13634
13700
  backend: Backend,
13635
13701
  claude: DispatchProviderPreferences,
13636
- codex: DispatchProviderPreferences
13702
+ codex: DispatchProviderPreferences,
13703
+ // Optional because every record written before the ACP backend existed lacks it, and a required
13704
+ // key would fail those records' parse and silently reset the operator's saved profile.
13705
+ acp: DispatchProviderPreferences.optional()
13637
13706
  });
13638
- DispatchProfileSnapshot = external_exports.object({
13707
+ DispatchProfileSnapshotShape = external_exports.object({
13639
13708
  backend: Backend,
13640
13709
  model: external_exports.string().trim().min(1).max(200),
13641
- effort: Settings.shape.effort.unwrap(),
13710
+ // Required for Claude and Codex (the refinement below); absent for an ACP agent, which runs with
13711
+ // whatever model and effort its own CLI is configured for — Frizz has no effort axis to offer there.
13712
+ effort: Settings.shape.effort,
13642
13713
  // IGNORED: dispatch permission is decided server-side (workerDispatchPermission) from the
13643
13714
  // non-interactive floor plus the operator's Settings choice, never per dispatch. Optional so old
13644
13715
  // clients that still send it parse.
13645
13716
  permissionMode: PermissionMode.optional()
13646
13717
  }).strict();
13718
+ DispatchProfileSnapshot = DispatchProfileSnapshotShape.superRefine(requireEffortOutsideAcp);
13647
13719
  SetDispatchPreferenceInput = external_exports.discriminatedUnion("field", [
13648
13720
  external_exports.object({ field: external_exports.literal("backend"), value: Backend }),
13649
13721
  external_exports.object({
13650
13722
  field: external_exports.literal("profile"),
13651
13723
  backend: Backend,
13652
13724
  model: external_exports.string().trim().min(1).max(200),
13653
- effort: Settings.shape.effort.unwrap()
13725
+ // Optional ONLY for an ACP profile (see DispatchProfileSnapshot); a Claude/Codex profile without
13726
+ // an effort is refused below rather than silently stored as "default".
13727
+ effort: Settings.shape.effort
13654
13728
  }),
13655
13729
  external_exports.object({ field: external_exports.literal("model"), backend: Backend, value: external_exports.string().trim().min(1).max(200) }),
13656
13730
  external_exports.object({ field: external_exports.literal("effort"), backend: Backend, value: Settings.shape.effort.unwrap() })
13657
- ]);
13731
+ ]).superRefine((update, ctx) => {
13732
+ if (update.field === "profile") requireEffortOutsideAcp(update, ctx);
13733
+ });
13658
13734
  DispatchInput = external_exports.object({
13659
13735
  // Optional: when omitted, dispatch derives a fallback title from the prompt (Claude later renames
13660
13736
  // the session via ai-title, which the UI prefers for display). The thread FILE always gets a
@@ -13806,7 +13882,7 @@ var init_src = __esm({
13806
13882
  timers: external_exports.array(ThreadTimerView)
13807
13883
  }).strict();
13808
13884
  ThreadActivityItem = external_exports.object({
13809
- kind: external_exports.enum(["shell", "agent", "timer", "pr"]),
13885
+ kind: external_exports.enum(["shell", "agent", "timer", "pr", "issue"]),
13810
13886
  /** The string a `<kind>:` fence line must carry. For a shell that is the runtime task id the worker
13811
13887
  * was shown; for a PR, `owner/repo#N`; for a timer, its `tmr_…` row id. */
13812
13888
  id: external_exports.string(),
@@ -14044,9 +14120,9 @@ ${DISPATCH_TASK_BANNER}
14044
14120
  page: external_exports.number().int().positive(),
14045
14121
  pageCount: external_exports.number().int().positive()
14046
14122
  });
14047
- GithubBatchInput = DispatchProfileSnapshot.extend({
14123
+ GithubBatchInput = DispatchProfileSnapshotShape.extend({
14048
14124
  items: external_exports.array(external_exports.object({ kind: external_exports.enum(["issue", "pr"]), number: external_exports.number().int().positive() })).min(1)
14049
- }).strict();
14125
+ }).strict().superRefine(requireEffortOutsideAcp);
14050
14126
  GithubBatchResult = external_exports.object({
14051
14127
  dispatched: external_exports.array(external_exports.object({ number: external_exports.number(), kind: external_exports.string(), slug: ThreadSlug })),
14052
14128
  failed: external_exports.array(external_exports.object({ number: external_exports.number(), kind: external_exports.string(), error: external_exports.string() }))
@@ -19486,11 +19562,13 @@ function readBootProgress(stateDir) {
19486
19562
  return null;
19487
19563
  }
19488
19564
  }
19489
- var BOOT_PROGRESS_NAME;
19565
+ var BOOT_PROGRESS_NAME, BOOT_STALL_TIMEOUT_MS, BOOT_HARD_TIMEOUT_MS;
19490
19566
  var init_boot_progress = __esm({
19491
19567
  "packages/server/src/boot-progress.ts"() {
19492
19568
  "use strict";
19493
19569
  BOOT_PROGRESS_NAME = "boot.progress";
19570
+ BOOT_STALL_TIMEOUT_MS = 3e4;
19571
+ BOOT_HARD_TIMEOUT_MS = 10 * 6e4;
19494
19572
  }
19495
19573
  });
19496
19574
 
@@ -24652,8 +24730,8 @@ init_logging();
24652
24730
  init_src();
24653
24731
  init_project_registry();
24654
24732
  var PORT_SCAN_COUNT = 100;
24655
- var LAUNCH_TIMEOUT_MS = 3e4;
24656
- var LAUNCH_HARD_TIMEOUT_MS = 10 * 6e4;
24733
+ var LAUNCH_TIMEOUT_MS = BOOT_STALL_TIMEOUT_MS;
24734
+ var LAUNCH_HARD_TIMEOUT_MS = BOOT_HARD_TIMEOUT_MS;
24657
24735
  var RETIRED_NETWORK_FLAGS = ["--host", "--allowed-host", "--public-origin", "--cloud"];
24658
24736
  function retiredFlagMessage(flag) {
24659
24737
  return `${flag} was retired \u2014 how a board is reached is set up from the running board now: press R in its terminal to pick a private or custom frizz.sh name, a Cloudflare Tunnel, Tailscale or a proxy of your own. The choice is remembered, so a plain launch serves it.`;
@@ -25187,12 +25265,13 @@ import { basename as basename6, dirname as dirname11, extname as extname2, isAbs
25187
25265
  import { fileURLToPath } from "node:url";
25188
25266
  import watcher from "@parcel/watcher";
25189
25267
  init_logging();
25268
+ init_boot_progress();
25190
25269
  var DEV_RESTART_DEBOUNCE_MS = 180;
25191
25270
  var DEV_CRASH_STABLE_MS = 5e3;
25192
25271
  var DEV_CRASH_RETRY_BASE_MS = 500;
25193
25272
  var DEV_CRASH_RETRY_MAX_MS = 1e4;
25194
25273
  var CHILD_STOP_TIMEOUT_MS = 15e3;
25195
- var STABLE_UPDATE_READY_TIMEOUT_MS = 3e4;
25274
+ var STABLE_UPDATE_READY_TIMEOUT_MS = BOOT_STALL_TIMEOUT_MS;
25196
25275
  var STABLE_UPDATE_STABILIZE_MS = 1e3;
25197
25276
  var CHILD_KILL_GRACE_MS = 5e3;
25198
25277
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".json"]);
@@ -25444,6 +25523,7 @@ var Supervisor = class {
25444
25523
  updateMode;
25445
25524
  commitUpdate;
25446
25525
  updateReadyTimeoutMs;
25526
+ updateHardTimeoutMs;
25447
25527
  updateStabilizeMs;
25448
25528
  rollbackUpdate;
25449
25529
  durableReexec;
@@ -25488,6 +25568,7 @@ var Supervisor = class {
25488
25568
  this.updateMode = opts.updateMode ?? "durableReexec";
25489
25569
  this.commitUpdate = opts.commitUpdate;
25490
25570
  this.updateReadyTimeoutMs = Math.max(1, opts.updateReadyTimeoutMs ?? STABLE_UPDATE_READY_TIMEOUT_MS);
25571
+ this.updateHardTimeoutMs = Math.max(1, opts.updateHardTimeoutMs ?? BOOT_HARD_TIMEOUT_MS);
25491
25572
  this.updateStabilizeMs = Math.max(0, opts.updateStabilizeMs ?? STABLE_UPDATE_STABILIZE_MS);
25492
25573
  this.rollbackUpdate = opts.rollbackUpdate;
25493
25574
  this.durableReexec = opts.durableReexec;
@@ -25732,7 +25813,8 @@ var Supervisor = class {
25732
25813
  if (this.closed) return this.failChildUpdate("Frizz supervisor stopped while starting the update", true);
25733
25814
  const ready = await this.spawnChild({
25734
25815
  updateCandidate: true,
25735
- readinessTimeoutMs: this.updateReadyTimeoutMs
25816
+ readinessTimeoutMs: this.updateReadyTimeoutMs,
25817
+ hardTimeoutMs: this.updateHardTimeoutMs
25736
25818
  });
25737
25819
  const candidate = this.child;
25738
25820
  if (!ready || !candidate) {
@@ -25912,9 +25994,28 @@ var Supervisor = class {
25912
25994
  settled(ready);
25913
25995
  };
25914
25996
  if (options2.readinessTimeoutMs !== void 0) {
25915
- readinessTimer = setTimeout(() => {
25997
+ const stallMs = options2.readinessTimeoutMs;
25998
+ const hardMs = options2.hardTimeoutMs ?? BOOT_HARD_TIMEOUT_MS;
25999
+ const hardDeadline = Date.now() + hardMs;
26000
+ let stallDeadline = Date.now() + stallMs;
26001
+ let lastStep = -1;
26002
+ let lastPhase;
26003
+ const checkProgress = () => {
25916
26004
  if (spawnSettled) return;
25917
- const message = `update candidate did not become ready within ${options2.readinessTimeoutMs}ms`;
26005
+ const now = Date.now();
26006
+ const progress = readBootProgress(this.launchTarget.stateDir);
26007
+ if (progress && progress.pid === child.pid && progress.step > lastStep) {
26008
+ lastStep = progress.step;
26009
+ lastPhase = progress.phase;
26010
+ stallDeadline = now + stallMs;
26011
+ }
26012
+ if (now < stallDeadline && now < hardDeadline) {
26013
+ readinessTimer = setTimeout(checkProgress, Math.min(250, stallDeadline - now, hardDeadline - now));
26014
+ readinessTimer.unref();
26015
+ return;
26016
+ }
26017
+ const reason = now >= hardDeadline ? `update candidate did not become ready within ${hardMs}ms` : `update candidate made no boot progress for ${stallMs}ms`;
26018
+ const message = lastPhase ? `${reason}; last boot step: ${lastPhase}` : reason;
25918
26019
  this.lastRestartFailure = message;
25919
26020
  this.errorLine(`[frizz] ${message}`);
25920
26021
  this.writeStatus("failed", message, null);
@@ -25923,8 +26024,8 @@ var Supervisor = class {
25923
26024
  } catch {
25924
26025
  }
25925
26026
  settleSpawn(false);
25926
- }, options2.readinessTimeoutMs);
25927
- readinessTimer.unref();
26027
+ };
26028
+ checkProgress();
25928
26029
  }
25929
26030
  child.on("message", (message) => {
25930
26031
  if (stopOwnerMessage(message) && message.token === this.ownerToken) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frizz",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "private": false,
5
5
  "description": "A local web UI for running many coding agents at once",
6
6
  "keywords": [
@@ -35,7 +35,7 @@
35
35
  "frizz-dev:check": "nub scripts/install-global-cli.mjs --check",
36
36
  "frizz-dev:uninstall": "nub scripts/install-global-cli.mjs --uninstall",
37
37
  "install:global": "nub scripts/install-global-cli.mjs",
38
- "typecheck": "tsc -b packages/shared packages/rpc packages/server packages/registrar packages/relay . packages/web",
38
+ "typecheck": "tsc -b packages/shared packages/rpc packages/claude-agent-sdk-runtime packages/server packages/registrar packages/relay . packages/web",
39
39
  "test": "nub scripts/run-tests.mjs",
40
40
  "test:e2e": "nub scripts/e2e-web.mjs",
41
41
  "prepack": "node ./scripts/prepare-package.mjs --server && node ./scripts/build-package.mjs --server && node ./scripts/build-package.mjs --shell && node ./scripts/publish-manifest.mjs --strip",
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "frizzServer": {
67
67
  "package": "frizz-server",
68
- "version": "0.13.0",
68
+ "version": "0.13.8",
69
69
  "protocol": 1,
70
70
  "dataEpoch": 1
71
71
  }