frizz 0.13.6 → 0.13.7

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 +240 -103
  2. package/package.json +2 -2
package/dist/frizz.js CHANGED
@@ -12485,6 +12485,106 @@ var init_shell_writes = __esm({
12485
12485
  });
12486
12486
 
12487
12487
  // packages/shared/src/index.ts
12488
+ function signoffNudgeAwaitingLines(needsInput) {
12489
+ if (!needsInput) {
12490
+ return [
12491
+ "- `` ```awaiting `` \u2014 you are WAITING on work that is actually running. FRONTMATTER, THEN MARKDOWN:",
12492
+ " one YAML list per kind of thing you are waiting on, a REQUIRED `for:` duration, then a `---` line and",
12493
+ " as much prose as you want. The prose is OPTIONAL; the lines above it are not.",
12494
+ "",
12495
+ " ```awaiting",
12496
+ " shells: [<the id your runtime gave you>]",
12497
+ " prs: [owner/repo#123]",
12498
+ " for: 2h",
12499
+ " ---",
12500
+ " What you are waiting for, in your own words \u2014 this is what the human reads on your card.",
12501
+ " ```"
12502
+ ];
12503
+ }
12504
+ return [
12505
+ "- `` ```awaiting `` \u2014 you are WAITING on work that is actually running: a background shell, a",
12506
+ " sub-agent, a timer or a registered PR. FRONTMATTER, THEN MARKDOWN: one YAML list per kind of thing",
12507
+ " you are waiting on, a REQUIRED `for:` duration, a REQUIRED `needs_input:` answer, then optionally a",
12508
+ " `---` line and prose.",
12509
+ "",
12510
+ " ```awaiting",
12511
+ " agents: [<the id your runtime gave you>]",
12512
+ " needs_input: false",
12513
+ " for: 2h",
12514
+ " ```",
12515
+ "",
12516
+ " `needs_input: false` \u2014 nothing for the human yet. The thread stays out of their queue until the work",
12517
+ " wakes you, and you owe NO write-up: the fence alone is the whole message. `needs_input: true` \u2014 the",
12518
+ " human can read, try or act on something NOW while the work runs (a partial result, a file you wrote,",
12519
+ " a server to try), even if you need nothing back from them; the thread goes into their queue, and the",
12520
+ " prose under `---` says what to look at. If you wrote ANY words for the human at this rest, it is",
12521
+ " `true` \u2014 a `false` rest is never put in front of them. A rest on running work with NO fence is a",
12522
+ " bare rest, and it lands in the human's queue."
12523
+ ];
12524
+ }
12525
+ function signoffNudgeText(needsInput) {
12526
+ return [
12527
+ `${SIGNOFF_NUDGE_MARKER} Nothing about your task has changed, and no new work is being asked of you.`,
12528
+ "",
12529
+ "You rested without a fence, so this thread cannot be triaged.",
12530
+ "",
12531
+ "**IF THE TASK STILL HAS PARTS LEFT, THE FENCE IS NOT WHAT YOU OWE \u2014 THE WORK IS.** If ANY part of the",
12532
+ "original task is unfinished, unverified, or deferred, resume it NOW, in THIS turn, and sign off once it",
12533
+ "is genuinely finished. A milestone, a green test run and a long turn are none of them endings, and",
12534
+ "neither is naming the next step or writing it into a scratch file.",
12535
+ "",
12536
+ "**AND THAT TASK IS ALSO THE CEILING \u2014 finish it, and nothing else.** Work you notice on the way is a",
12537
+ "FINDING TO REPORT in your sign-off, never work to take on: the bug beside the one you were sent for,",
12538
+ "the refactor the code obviously wants, the second issue the first one touches. Widening the job is not",
12539
+ "thoroughness \u2014 it is a different job nobody asked for, and it buries the answer they did ask for under",
12540
+ "changes they now have to review. If it should be done, name it in one line and let the human dispatch",
12541
+ "it.",
12542
+ "",
12543
+ "**IF WHAT YOU WERE ASKED FOR IS A DOCUMENT, THE DOCUMENT IS THE ENDING.** A triage, a review, an",
12544
+ "investigation, a recommendation, a plan \u2014 when that is the deliverable, the finished write-up IS the",
12545
+ "work. Implementing what it proposes is the NEXT job, and not yours unless you were asked. Sign off with",
12546
+ "the write-up.",
12547
+ "",
12548
+ "**DECIDE RATHER THAN ASK.** Stop only for a decision that is genuinely the human's AND that blocks you",
12549
+ "right now: register that one with `mcp__frizz__ask`. Every other open choice INSIDE the task \u2014 a name, a",
12550
+ "default, a reversible design call \u2014 is yours to make: decide it, say in one line which way you went and",
12551
+ "what would reverse it, and carry on. A choice that would ENLARGE the task is not one of those: an",
12552
+ "unanswered question is not permission to go build the answer.",
12553
+ "",
12554
+ "Otherwise, sign off \u2014 a registration, or a fence at the END of your next message:",
12555
+ "",
12556
+ "- `mcp__frizz__ask` \u2014 you need the human. NOT a fence: the ```question fence is retired, and a fence",
12557
+ " with a question in its body is plain prose. Register it (options with one-line trade-offs, the",
12558
+ " recommended one first), then rest normally \u2014 an open registered question is the sign-off.",
12559
+ "- `` ```done `` \u2014 genuinely FINISHED. A DISMISSAL: the card is filed away and nobody looks again, so",
12560
+ " if anything is still owed, it is not done. Body: 1-3 sentences, then bullets, each opening with a",
12561
+ " **bolded verb phrase**.",
12562
+ ...signoffNudgeAwaitingLines(needsInput),
12563
+ "",
12564
+ " Frizz CHECKS every line: name something that is not running and you are bumped rather than parked.",
12565
+ " A fence that names NOTHING is not a park at all \u2014 if you are not waiting on anything, you are not",
12566
+ " awaiting, you are done. Register a PR with `mcp__frizz__watch_pr` and a timer with",
12567
+ " `mcp__frizz__timer`; `mcp__frizz__activity` reads back everything you have running, with its id.",
12568
+ "",
12569
+ "**STILL OWED counts things you are not going to do yourself.** A decision you are RECOMMENDING, a",
12570
+ "draft you wrote but did not send, follow-up work you discovered \u2014 all of it dies with the card, even",
12571
+ "the part that is someone else's to do. Each ends on a question with your recommendation as option A,",
12572
+ "or you DO it first \u2014 a sub-agent's result comes BACK to you, so it lands on your card; a new card via",
12573
+ "`mcp__frizz__spawn_thread` is the LAST resort, since nothing it learns returns to you or its siblings.",
12574
+ "None of them is a `done`. And what is not",
12575
+ 'worth a card is not worth a SENTENCE: delete the dangling "one thing to carry forward", never park it',
12576
+ "in the handoff.",
12577
+ "",
12578
+ "**DO NOT REPEAT YOURSELF.** If the message you just wrote already stands on its own, reply with the",
12579
+ "fence ALONE \u2014 the human reads both together, so restating it costs them the second read for nothing.",
12580
+ "The same holds inside one message: the card is the ledger of what shipped, the prose is only what a",
12581
+ "ledger cannot hold, and a sentence that reads the same in either belongs in exactly one of them.",
12582
+ "",
12583
+ "Only if it does NOT stand alone, fix that first, briefly. It has to be readable cold: the human has",
12584
+ "seen nothing since their own last message \u2014 the Goal, this reminder, a watcher wake all came from",
12585
+ "frizz \u2014 so anything you assumed they had followed, they have not."
12586
+ ].join("\n");
12587
+ }
12488
12588
  function requireEffortOutsideAcp(profile, ctx) {
12489
12589
  if (profile.backend !== "acp" && profile.effort === void 0) {
12490
12590
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["effort"], message: `effort is required for a ${profile.backend} profile` });
@@ -12493,7 +12593,7 @@ function requireEffortOutsideAcp(profile, ctx) {
12493
12593
  function fallbackPort(base) {
12494
12594
  return base + 1e4;
12495
12595
  }
12496
- 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, ClaudeModel, 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, SettledQuestionView, ThreadSettledQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, LiveTool, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, DismissFailedFollowUpInput, DismissFailedFollowUpResult, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, CompactThreadInput, 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, UpgradeThreadModelInput, UpgradeThreadModelResult, 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, ProjectRailCounts, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
12596
+ 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, ClaudeModel, 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, SIGNOFF_NUDGE_MESSAGE_NEEDS_INPUT, 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, SettledQuestionView, ThreadSettledQuestionsResult, MarkOwnDoneInput, MarkOwnDoneResult, ListOwnPrWatchesInput, OwnPrWatchesResult, ThreadTimerView, ThreadFence, LimitWindow, LimitPause, ProviderError, ThreadLinkView, UpsertOwnLinkInput, UpsertOwnLinkResult, DropOwnLinkInput, DropOwnLinkResult, LiveTool, ThreadView, BoardErrorItem, BoardSnapshot, QuotaWindow, ProviderQuota, QuotaSnapshot, ProviderAuth, AccountEmails, AuthSnapshot, AccountBackend, AccountLogoutInput, AccountLogoutResult, PermissionMode, LocalFileOpener, Settings, DispatchProviderPreferences, DispatchPreferences, DispatchProfileSnapshotShape, DispatchProfileSnapshot, SetDispatchPreferenceInput, DispatchInput, ADOPT_THREAD_MESSAGE_MAX_CHARS, AdoptThreadInput, AdoptThreadResult, AdoptSessionInput, FollowUpInput, DismissFailedFollowUpInput, DismissFailedFollowUpResult, UnqueueFollowUpInput, UnqueueFollowUpResult, DeliverQueuedNowInput, DeliverQueuedNowResult, CompactThreadInput, 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, UpgradeThreadModelInput, UpgradeThreadModelResult, 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, ProjectRailCounts, PROJECT_ICON_MAX_BASE64_CHARS, DirectoryPickResult, ThreadLocation, FRIZZ_ROUTE_PREFIX, DEFAULT_PORT;
12497
12597
  var init_src = __esm({
12498
12598
  "packages/shared/src/index.ts"() {
12499
12599
  "use strict";
@@ -12831,7 +12931,7 @@ var init_src = __esm({
12831
12931
  questions: external_exports.array(AskQuestion)
12832
12932
  });
12833
12933
  AwaitingHint = external_exports.object({
12834
- kind: external_exports.enum(["shell", "agent", "timer", "pr", "issue", "for", "title"]),
12934
+ kind: external_exports.enum(["shell", "agent", "timer", "pr", "issue", "for", "title", "needs_input"]),
12835
12935
  value: external_exports.string()
12836
12936
  });
12837
12937
  RETIRED_AWAITING_KINDS = ["watch", "pr-watch", "human", "ci", "session", "shell", "agent", "timer", "pr", "reason"];
@@ -12870,77 +12970,8 @@ var init_src = __esm({
12870
12970
  TIMER_MAX_DELAY_SECONDS = 30 * 24 * 60 * 60;
12871
12971
  TimerPromptText = external_exports.string().trim().min(1).max(TIMER_PROMPT_MAX);
12872
12972
  SIGNOFF_NUDGE_MARKER = "**This message is from frizz, not from the human.**";
12873
- SIGNOFF_NUDGE_MESSAGE = [
12874
- `${SIGNOFF_NUDGE_MARKER} Nothing about your task has changed, and no new work is being asked of you.`,
12875
- "",
12876
- "You rested without a fence, so this thread cannot be triaged.",
12877
- "",
12878
- "**IF THE TASK STILL HAS PARTS LEFT, THE FENCE IS NOT WHAT YOU OWE \u2014 THE WORK IS.** If ANY part of the",
12879
- "original task is unfinished, unverified, or deferred, resume it NOW, in THIS turn, and sign off once it",
12880
- "is genuinely finished. A milestone, a green test run and a long turn are none of them endings, and",
12881
- "neither is naming the next step or writing it into a scratch file.",
12882
- "",
12883
- "**AND THAT TASK IS ALSO THE CEILING \u2014 finish it, and nothing else.** Work you notice on the way is a",
12884
- "FINDING TO REPORT in your sign-off, never work to take on: the bug beside the one you were sent for,",
12885
- "the refactor the code obviously wants, the second issue the first one touches. Widening the job is not",
12886
- "thoroughness \u2014 it is a different job nobody asked for, and it buries the answer they did ask for under",
12887
- "changes they now have to review. If it should be done, name it in one line and let the human dispatch",
12888
- "it.",
12889
- "",
12890
- "**IF WHAT YOU WERE ASKED FOR IS A DOCUMENT, THE DOCUMENT IS THE ENDING.** A triage, a review, an",
12891
- "investigation, a recommendation, a plan \u2014 when that is the deliverable, the finished write-up IS the",
12892
- "work. Implementing what it proposes is the NEXT job, and not yours unless you were asked. Sign off with",
12893
- "the write-up.",
12894
- "",
12895
- "**DECIDE RATHER THAN ASK.** Stop only for a decision that is genuinely the human's AND that blocks you",
12896
- "right now: register that one with `mcp__frizz__ask`. Every other open choice INSIDE the task \u2014 a name, a",
12897
- "default, a reversible design call \u2014 is yours to make: decide it, say in one line which way you went and",
12898
- "what would reverse it, and carry on. A choice that would ENLARGE the task is not one of those: an",
12899
- "unanswered question is not permission to go build the answer.",
12900
- "",
12901
- "Otherwise, sign off \u2014 a registration, or a fence at the END of your next message:",
12902
- "",
12903
- "- `mcp__frizz__ask` \u2014 you need the human. NOT a fence: the ```question fence is retired, and a fence",
12904
- " with a question in its body is plain prose. Register it (options with one-line trade-offs, the",
12905
- " recommended one first), then rest normally \u2014 an open registered question is the sign-off.",
12906
- "- `` ```done `` \u2014 genuinely FINISHED. A DISMISSAL: the card is filed away and nobody looks again, so",
12907
- " if anything is still owed, it is not done. Body: 1-3 sentences, then bullets, each opening with a",
12908
- " **bolded verb phrase**.",
12909
- "- `` ```awaiting `` \u2014 you are WAITING on work that is actually running. FRONTMATTER, THEN MARKDOWN:",
12910
- " one YAML list per kind of thing you are waiting on, a REQUIRED `for:` duration, then a `---` line and",
12911
- " as much prose as you want. The prose is OPTIONAL; the lines above it are not.",
12912
- "",
12913
- " ```awaiting",
12914
- " shells: [<the id your runtime gave you>]",
12915
- " prs: [owner/repo#123]",
12916
- " for: 2h",
12917
- " ---",
12918
- " What you are waiting for, in your own words \u2014 this is what the human reads on your card.",
12919
- " ```",
12920
- "",
12921
- " Frizz CHECKS every line: name something that is not running and you are bumped rather than parked.",
12922
- " A fence that names NOTHING is not a park at all \u2014 if you are not waiting on anything, you are not",
12923
- " awaiting, you are done. Register a PR with `mcp__frizz__watch_pr` and a timer with",
12924
- " `mcp__frizz__timer`; `mcp__frizz__activity` reads back everything you have running, with its id.",
12925
- "",
12926
- "**STILL OWED counts things you are not going to do yourself.** A decision you are RECOMMENDING, a",
12927
- "draft you wrote but did not send, follow-up work you discovered \u2014 all of it dies with the card, even",
12928
- "the part that is someone else's to do. Each ends on a question with your recommendation as option A,",
12929
- "or you DO it first \u2014 a sub-agent's result comes BACK to you, so it lands on your card; a new card via",
12930
- "`mcp__frizz__spawn_thread` is the LAST resort, since nothing it learns returns to you or its siblings.",
12931
- "None of them is a `done`. And what is not",
12932
- 'worth a card is not worth a SENTENCE: delete the dangling "one thing to carry forward", never park it',
12933
- "in the handoff.",
12934
- "",
12935
- "**DO NOT REPEAT YOURSELF.** If the message you just wrote already stands on its own, reply with the",
12936
- "fence ALONE \u2014 the human reads both together, so restating it costs them the second read for nothing.",
12937
- "The same holds inside one message: the card is the ledger of what shipped, the prose is only what a",
12938
- "ledger cannot hold, and a sentence that reads the same in either belongs in exactly one of them.",
12939
- "",
12940
- "Only if it does NOT stand alone, fix that first, briefly. It has to be readable cold: the human has",
12941
- "seen nothing since their own last message \u2014 the Goal, this reminder, a watcher wake all came from",
12942
- "frizz \u2014 so anything you assumed they had followed, they have not."
12943
- ].join("\n");
12973
+ SIGNOFF_NUDGE_MESSAGE = signoffNudgeText(false);
12974
+ SIGNOFF_NUDGE_MESSAGE_NEEDS_INPUT = signoffNudgeText(true);
12944
12975
  ThreadWatchKind = external_exports.enum(["shell", "github", "timer"]);
12945
12976
  GithubChecksState = external_exports.enum(["none", "running", "passing", "failing"]);
12946
12977
  GithubMergeState = external_exports.enum(["mergeable", "blocked", "conflicting", "unknown"]);
@@ -22333,13 +22364,26 @@ var qrcode_default = qrcode;
22333
22364
  var stringToBytes = qrcode.stringToBytes;
22334
22365
 
22335
22366
  // packages/server/src/qr.ts
22336
- var LIGHT_BG = "\x1B[48;5;15m";
22337
- var DARK = "\x1B[38;5;0m";
22338
- var DARK_BG = "\x1B[48;5;0m";
22367
+ var DARK = 232;
22368
+ var LIGHT = 231;
22369
+ var fg = (dark) => `\x1B[38;5;${dark ? DARK : LIGHT}m`;
22370
+ var bg = (dark) => `\x1B[48;5;${dark ? DARK : LIGHT}m`;
22339
22371
  var RESET = "\x1B[0m";
22340
22372
  var UPPER_HALF = "\u2580";
22341
22373
  var LOWER_HALF = "\u2584";
22342
22374
  var QUIET_ZONE = 4;
22375
+ var BLOCK_DRAWING_PROGRAMS = /* @__PURE__ */ new Set(["iTerm.app", "WezTerm", "ghostty", "WarpTerminal", "vscode"]);
22376
+ var BLOCK_DRAWING_TERMS = /* @__PURE__ */ new Set(["xterm-kitty", "xterm-ghostty", "alacritty", "wezterm"]);
22377
+ function drawsBlockElements(env = process.env) {
22378
+ const term = env.TERM ?? "";
22379
+ if (env.TMUX || env.STY || env.TERM_PROGRAM === "tmux" || term.startsWith("screen") || term.startsWith("tmux")) return false;
22380
+ if (BLOCK_DRAWING_PROGRAMS.has(env.TERM_PROGRAM ?? "")) return true;
22381
+ if (BLOCK_DRAWING_TERMS.has(term)) return true;
22382
+ if (env.LC_TERMINAL === "iTerm2") return true;
22383
+ return Boolean(
22384
+ env.KITTY_WINDOW_ID || env.ALACRITTY_WINDOW_ID || env.WEZTERM_PANE || env.GHOSTTY_RESOURCES_DIR || env.WT_SESSION || env.VTE_VERSION
22385
+ );
22386
+ }
22343
22387
  function encode(value, errorCorrection) {
22344
22388
  const code = qrcode_default(0, errorCorrection);
22345
22389
  code.addData(value);
@@ -22356,26 +22400,70 @@ function encode(value, errorCorrection) {
22356
22400
  }
22357
22401
  };
22358
22402
  }
22359
- function renderQrLines(value, options2 = {}) {
22360
- if (!value) throw new Error("renderQr requires a value");
22361
- const { size, at } = encode(value, options2.errorCorrection ?? "M");
22403
+ function sizeOf(size, style) {
22404
+ return style === "paint" ? { columns: size * 2, rows: size } : { columns: size, rows: Math.ceil(size / 2) };
22405
+ }
22406
+ function qrSize(value, style = "half", errorCorrection = "M") {
22407
+ return sizeOf(encode(value, errorCorrection).size, style);
22408
+ }
22409
+ function qrStyleFor(value, options2 = {}) {
22410
+ const { area } = options2;
22411
+ if (!area || drawsBlockElements(options2.env)) return "half";
22412
+ const paint = qrSize(value, "paint", options2.errorCorrection);
22413
+ return paint.columns <= area.columns && paint.rows <= area.rows ? "paint" : "half";
22414
+ }
22415
+ function qrAreaOf(stream, frame) {
22416
+ if (!stream.columns || !stream.rows) return void 0;
22417
+ return { columns: stream.columns - frame.indent, rows: stream.rows - frame.rows };
22418
+ }
22419
+ function halfLines(size, at, plain) {
22362
22420
  const lines = [];
22363
22421
  for (let y = 0; y < size; y += 2) {
22364
22422
  let line = "";
22365
22423
  for (let x = 0; x < size; x++) {
22366
22424
  const top = at(x, y);
22367
22425
  const bottom = y + 1 < size ? at(x, y + 1) : false;
22368
- if (options2.plain) {
22426
+ if (plain) {
22369
22427
  line += top && bottom ? "#" : top ? "^" : bottom ? "v" : " ";
22370
22428
  continue;
22371
22429
  }
22372
- if (top === bottom) line += `${top ? DARK_BG : LIGHT_BG} `;
22373
- else line += `${DARK}${LIGHT_BG}${top ? UPPER_HALF : LOWER_HALF}`;
22430
+ if (top === bottom) {
22431
+ line += `${bg(top)} `;
22432
+ continue;
22433
+ }
22434
+ const above = y > 0 ? at(x, y - 1) : false;
22435
+ const below = y + 2 < size ? at(x, y + 2) : false;
22436
+ line += below === bottom && above !== top ? `${fg(top)}${bg(bottom)}${UPPER_HALF}` : `${fg(bottom)}${bg(top)}${LOWER_HALF}`;
22374
22437
  }
22375
- lines.push(options2.plain ? line : `${line}${RESET}`);
22438
+ lines.push(plain ? line : `${line}${RESET}`);
22376
22439
  }
22377
22440
  return lines;
22378
22441
  }
22442
+ function paintLines(size, at, plain) {
22443
+ const lines = [];
22444
+ for (let y = 0; y < size; y++) {
22445
+ let line = "";
22446
+ let last;
22447
+ for (let x = 0; x < size; x++) {
22448
+ const dark = at(x, y);
22449
+ if (plain) line += dark ? "##" : " ";
22450
+ else {
22451
+ if (dark !== last) line += bg(dark);
22452
+ line += " ";
22453
+ }
22454
+ last = dark;
22455
+ }
22456
+ lines.push(plain ? line : `${line}${RESET}`);
22457
+ }
22458
+ return lines;
22459
+ }
22460
+ function renderQrLines(value, options2 = {}) {
22461
+ if (!value) throw new Error("renderQr requires a value");
22462
+ const errorCorrection = options2.errorCorrection ?? "M";
22463
+ const { size, at } = encode(value, errorCorrection);
22464
+ const style = options2.style ?? qrStyleFor(value, { area: options2.area, env: options2.env, errorCorrection });
22465
+ return style === "paint" ? paintLines(size, at, options2.plain ?? false) : halfLines(size, at, options2.plain ?? false);
22466
+ }
22379
22467
 
22380
22468
  // packages/server/src/restart-supervisor.ts
22381
22469
  import { request as requestHttp, createServer } from "node:http";
@@ -23298,12 +23386,20 @@ function installPaneHost(options2) {
23298
23386
  input.pause();
23299
23387
  process.off("exit", restore);
23300
23388
  };
23389
+ const reclaim = () => {
23390
+ if (disposed) return;
23391
+ try {
23392
+ input.setRawMode(false);
23393
+ input.setRawMode(true);
23394
+ } catch {
23395
+ }
23396
+ };
23301
23397
  input.setRawMode(true);
23302
23398
  input.resume();
23303
23399
  input.setEncoding("utf8");
23304
23400
  input.on("data", onKey);
23305
23401
  process.on("exit", restore);
23306
- return { dispose: restore, isOpen: () => active !== null };
23402
+ return { dispose: restore, isOpen: () => active !== null, reclaim };
23307
23403
  }
23308
23404
 
23309
23405
  // src/access-pane.ts
@@ -23317,6 +23413,18 @@ var RESET2 = "\x1B[0m";
23317
23413
  function secondsUntil(expiresAt, now) {
23318
23414
  return Math.max(0, Math.ceil((expiresAt - now) / 1e3));
23319
23415
  }
23416
+ function fitScreen(entries, rows, columns) {
23417
+ const width = columns ?? 80;
23418
+ const height = (text) => Math.max(1, Math.ceil([...text.replace(/\x1b\[[0-9;]*m/g, "")].length / width));
23419
+ const kept = entries.map((entry) => typeof entry === "string" ? { text: entry, drop: Infinity } : entry);
23420
+ const total = () => kept.reduce((sum, entry) => sum + height(entry.text), 0);
23421
+ while (rows && total() > rows) {
23422
+ const next = kept.reduce((lowest, entry, index) => entry.drop < (kept[lowest]?.drop ?? Infinity) ? index : lowest, -1);
23423
+ if (next < 0) break;
23424
+ kept.splice(next, 1);
23425
+ }
23426
+ return kept.map((entry) => entry.text).join("\n");
23427
+ }
23320
23428
  function createAccessPane(options2) {
23321
23429
  const output = options2.output ?? process.stdout;
23322
23430
  const now = options2.now ?? Date.now;
@@ -23328,16 +23436,25 @@ function createAccessPane(options2) {
23328
23436
  if (!shown) return;
23329
23437
  const remaining = secondsUntil(shown.expiresAt, now());
23330
23438
  const status = consumed ? "This link has been used. Press L for another." : remaining === 0 ? "This link has expired. Press L for another." : `Single use, expires in ${remaining}s.`;
23439
+ const footer = `${status} Press any other key to close.`;
23440
+ const width = output.columns ?? 80;
23441
+ const wrapped = (text) => Math.max(1, Math.ceil((text.length + 2) / width));
23442
+ const qr = renderQrLines(shown.url, { area: qrAreaOf(output, { indent: 2, rows: wrapped(shown.url) + wrapped(footer) + 1 }) });
23331
23443
  output.write(CLEAR);
23332
- output.write("\n");
23333
- for (const row of renderQrLines(shown.url)) output.write(` ${row}
23334
- `);
23335
- output.write(`
23336
- ${shown.url}
23337
- `);
23338
- output.write(`
23339
- ${DIM}${status} Press any other key to close.${RESET2}
23340
- `);
23444
+ output.write(
23445
+ fitScreen(
23446
+ [
23447
+ { text: "", drop: 1 },
23448
+ ...qr.map((row) => ` ${row}`),
23449
+ { text: "", drop: 3 },
23450
+ ` ${shown.url}`,
23451
+ { text: "", drop: 2 },
23452
+ ` ${DIM}${footer}${RESET2}`
23453
+ ],
23454
+ output.rows,
23455
+ output.columns
23456
+ )
23457
+ );
23341
23458
  };
23342
23459
  return {
23343
23460
  open() {
@@ -24284,9 +24401,8 @@ function createRemotePane(options2) {
24284
24401
  const write = (lines) => {
24285
24402
  if (!open) return;
24286
24403
  output.write(CLEAR);
24287
- output.write("\n");
24288
- for (const line of lines) output.write(` ${line}
24289
- `);
24404
+ const indented = lines.map((line) => typeof line === "string" ? ` ${line}` : { ...line, text: ` ${line.text}` });
24405
+ output.write(fitScreen([{ text: "", drop: 0 }, ...indented], output.rows, output.columns));
24290
24406
  };
24291
24407
  const check = (ok, text) => ok ? `${text} \u2713` : text;
24292
24408
  const paint = () => {
@@ -24328,13 +24444,25 @@ function createRemotePane(options2) {
24328
24444
  return;
24329
24445
  }
24330
24446
  if (s.name === "done") {
24331
- const lines = [s.message, ""];
24332
- if (s.link) {
24333
- for (const row of renderQrLines(s.link.url)) lines.push(row);
24334
- lines.push("", s.link.url, "", `${DIM}Scan to sign in on a phone. Single use, expires in 5 minutes. Press L later for another.${RESET2}`);
24447
+ if (!s.link) {
24448
+ write([s.message, "", `${DIM}press any key to return${RESET2}`]);
24449
+ return;
24335
24450
  }
24336
- lines.push("", `${DIM}press any key to return${RESET2}`);
24337
- write(lines);
24451
+ const note = `${DIM}Scan to sign in on a phone. Single use, expires in 5 minutes. Press L later for another.${RESET2}`;
24452
+ const width = output.columns ?? 80;
24453
+ const wrapped = (text) => Math.max(1, Math.ceil((text.length + 2) / width));
24454
+ const qr = renderQrLines(s.link.url, { area: qrAreaOf(output, { indent: 2, rows: wrapped(s.message) + 1 + wrapped(s.link.url) + 1 }) });
24455
+ write([
24456
+ s.message,
24457
+ { text: "", drop: 4 },
24458
+ ...qr,
24459
+ { text: "", drop: 6 },
24460
+ s.link.url,
24461
+ { text: "", drop: 2 },
24462
+ { text: note, drop: 5 },
24463
+ { text: "", drop: 3 },
24464
+ `${DIM}press any key to return${RESET2}`
24465
+ ]);
24338
24466
  return;
24339
24467
  }
24340
24468
  write([`Could not apply that: ${s.message}`, "", `${DIM}press any key to go back${RESET2}`]);
@@ -25699,6 +25827,7 @@ var Supervisor = class {
25699
25827
  logLine;
25700
25828
  errorLine;
25701
25829
  onActivity;
25830
+ onChildExit;
25702
25831
  /** Suppresses activity beats until the first boot has settled; until then the readout owns the terminal. */
25703
25832
  booted = false;
25704
25833
  /** When the restart currently in flight began, so its "ready" beat can report a duration. */
@@ -25751,6 +25880,7 @@ var Supervisor = class {
25751
25880
  this.logLine = opts.log ?? ((line) => log.info("supervisor", stripPrefix(line)));
25752
25881
  this.errorLine = opts.error ?? ((line) => log.error("supervisor", stripPrefix(line)));
25753
25882
  this.onActivity = opts.onActivity;
25883
+ this.onChildExit = opts.onChildExit;
25754
25884
  this.updateRestart = opts.updateRestart;
25755
25885
  this.updateMode = opts.updateMode ?? "durableReexec";
25756
25886
  this.commitUpdate = opts.commitUpdate;
@@ -26249,6 +26379,10 @@ var Supervisor = class {
26249
26379
  settleSpawn(false);
26250
26380
  });
26251
26381
  child.once("exit", (code, signal) => {
26382
+ try {
26383
+ this.onChildExit?.();
26384
+ } catch {
26385
+ }
26252
26386
  if (this.child === child) {
26253
26387
  this.child = null;
26254
26388
  this.childPort = void 0;
@@ -27217,7 +27351,7 @@ if (options.link) {
27217
27351
  process.exit(1);
27218
27352
  }
27219
27353
  const { url } = await response.json();
27220
- if (process.stdout.isTTY) for (const row of renderQrLines(url)) console.log(` ${row}`);
27354
+ if (process.stdout.isTTY) for (const row of renderQrLines(url, { area: qrAreaOf(process.stdout, { indent: 2, rows: 3 }) })) console.log(` ${row}`);
27221
27355
  console.log(process.stdout.isTTY ? `
27222
27356
  ${url}` : url);
27223
27357
  process.exit(0);
@@ -27395,6 +27529,9 @@ async function runSupervisor(port, token, onPrepared = () => {
27395
27529
  // here is triggered from a browser tab or by a crash, so without this the foreground process is
27396
27530
  // the last place to learn what happened to it.
27397
27531
  onActivity: (event) => renderSupervisorActivity(activityReadout, event),
27532
+ // Every child leaves the terminal as it found it at its own fork, and the first one was forked
27533
+ // before the keyboard went raw — so without this R stopped working after the first update.
27534
+ onChildExit: () => paneHost?.reclaim(),
27398
27535
  updateRestart: async () => {
27399
27536
  previous = active;
27400
27537
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frizz",
3
- "version": "0.13.6",
3
+ "version": "0.13.7",
4
4
  "private": false,
5
5
  "description": "A local web UI for running many coding agents at once",
6
6
  "keywords": [
@@ -66,7 +66,7 @@
66
66
  },
67
67
  "frizzServer": {
68
68
  "package": "frizz-server",
69
- "version": "0.15.7",
69
+ "version": "0.15.8",
70
70
  "protocol": 1,
71
71
  "dataEpoch": 1
72
72
  }