toolroll 0.9.67 → 0.9.68

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 (236) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/acp-client.js +1 -3
  3. package/dist/agent-board.d.ts +0 -1
  4. package/dist/agent-board.js +0 -1
  5. package/dist/agent-fence.js +3 -0
  6. package/dist/browser/workspace.css +1 -1
  7. package/dist/browser/workspace.js +31 -29
  8. package/dist/builder.js +3 -26
  9. package/dist/canonical-json.d.ts +14 -0
  10. package/dist/canonical-json.js +36 -0
  11. package/dist/chat-actions.js +1 -2
  12. package/dist/chat-admission.d.ts +19 -0
  13. package/dist/chat-admission.js +22 -0
  14. package/dist/chat-channel.js +1 -1
  15. package/dist/chat-delivery.js +0 -1
  16. package/dist/chat-follow.d.ts +17 -0
  17. package/dist/chat-follow.js +32 -0
  18. package/dist/chat-messages.js +0 -1
  19. package/dist/chat-settings-page.d.ts +23 -0
  20. package/dist/chat-settings-page.js +41 -0
  21. package/dist/claim.js +3 -6
  22. package/dist/claude-print.d.ts +12 -0
  23. package/dist/claude-print.js +34 -0
  24. package/dist/cli-http.d.ts +0 -7
  25. package/dist/cli-http.js +0 -21
  26. package/dist/cli.d.ts +3 -10
  27. package/dist/cli.js +15 -24
  28. package/dist/coding-provider.js +2 -1
  29. package/dist/coding-workspace.js +1 -1
  30. package/dist/{team-cli.d.ts → connect-cli.d.ts} +5 -9
  31. package/dist/connect-cli.js +331 -0
  32. package/dist/containment.d.ts +0 -2
  33. package/dist/containment.js +2 -6
  34. package/dist/contracts/chat-actions.d.ts +0 -1
  35. package/dist/contracts/chat-content.d.ts +0 -4
  36. package/dist/contracts/chat-content.js +0 -2
  37. package/dist/contracts/chat-tables.d.ts +0 -1
  38. package/dist/contracts/cli.d.ts +0 -62
  39. package/dist/contracts/cli.js +0 -4
  40. package/dist/contracts/coding-workspace.js +1 -1
  41. package/dist/contracts/contract.js +1 -1
  42. package/dist/contracts/flow-card.d.ts +0 -1
  43. package/dist/contracts/flow-propose.d.ts +0 -1
  44. package/dist/contracts/flow-send.js +1 -1
  45. package/dist/contracts/gateway-tools.d.ts +0 -1
  46. package/dist/contracts/integration-metadata.js +1 -1
  47. package/dist/contracts/lead-tools.d.ts +0 -1
  48. package/dist/contracts/memory-pass.d.ts +0 -1
  49. package/dist/contracts/memory-pass.js +1 -1
  50. package/dist/contracts/plan.js +1 -1
  51. package/dist/contracts/project-knowledge.js +1 -1
  52. package/dist/contracts/project-skills.d.ts +0 -1
  53. package/dist/contracts/proof.js +1 -1
  54. package/dist/contracts/recipes.js +1 -1
  55. package/dist/contracts/scope.js +1 -1
  56. package/dist/contracts/scout-report.js +1 -1
  57. package/dist/contracts/teams-callback.d.ts +0 -1
  58. package/dist/converse.d.ts +0 -6
  59. package/dist/converse.js +0 -19
  60. package/dist/daemon.js +3 -2
  61. package/dist/decision.d.ts +1 -3
  62. package/dist/deprecations.d.ts +12 -4
  63. package/dist/deprecations.js +27 -4
  64. package/dist/desktop-update-recovery.js +1 -1
  65. package/dist/desktop-update.js +3 -2
  66. package/dist/discord-chat.js +4 -15
  67. package/dist/discord-settings.d.ts +2 -6
  68. package/dist/discord-settings.js +10 -38
  69. package/dist/discord.js +12 -23
  70. package/dist/evidence.d.ts +0 -1
  71. package/dist/evidence.js +0 -3
  72. package/dist/exec.d.ts +0 -9
  73. package/dist/exec.js +3 -81
  74. package/dist/flow-actions.js +0 -3
  75. package/dist/flow-draft.d.ts +1 -3
  76. package/dist/flow-draft.js +15 -28
  77. package/dist/flow-scripts.d.ts +1 -8
  78. package/dist/flow-scripts.js +0 -13
  79. package/dist/flow-send.js +1 -1
  80. package/dist/flow-share.d.ts +0 -2
  81. package/dist/flow-share.js +1 -6
  82. package/dist/flow-steps.js +1 -1
  83. package/dist/flow-triggers.d.ts +0 -1
  84. package/dist/flow-triggers.js +1 -2
  85. package/dist/flow-unattended.js +1 -12
  86. package/dist/flows.js +1 -1
  87. package/dist/health-cli.d.ts +2 -2
  88. package/dist/health-cli.js +1 -1
  89. package/dist/invoke.d.ts +0 -3
  90. package/dist/invoke.js +5 -3
  91. package/dist/is-record.d.ts +2 -0
  92. package/dist/is-record.js +2 -0
  93. package/dist/lead-doors.d.ts +1 -5
  94. package/dist/lead-doors.js +0 -4
  95. package/dist/lead-tools.js +3 -6
  96. package/dist/live-detail.d.ts +0 -1
  97. package/dist/live-detail.js +0 -6
  98. package/dist/live-terminal.js +2 -1
  99. package/dist/live.d.ts +0 -1
  100. package/dist/live.js +1 -2
  101. package/dist/mcp-core.js +0 -2
  102. package/dist/memory-pass.js +1 -1
  103. package/dist/model-catalog.js +1 -1
  104. package/dist/modes.js +1 -12
  105. package/dist/operate.js +383 -750
  106. package/dist/password-bearer.d.ts +0 -2
  107. package/dist/password-bearer.js +0 -4
  108. package/dist/peek-cli.js +2 -1
  109. package/dist/peek.d.ts +2 -26
  110. package/dist/peek.js +2 -168
  111. package/dist/phase-routing.d.ts +0 -4
  112. package/dist/phase-routing.js +2 -21
  113. package/dist/planner-source.d.ts +2 -1
  114. package/dist/planner-source.js +2 -12
  115. package/dist/planner.js +2 -14
  116. package/dist/process-tree.d.ts +0 -2
  117. package/dist/process-tree.js +0 -10
  118. package/dist/project-delete.d.ts +0 -2
  119. package/dist/project-delete.js +1 -3
  120. package/dist/project-learning.d.ts +0 -14
  121. package/dist/project-skills.d.ts +0 -1
  122. package/dist/project-skills.js +0 -14
  123. package/dist/project-tools.js +2 -10
  124. package/dist/provider.d.ts +0 -17
  125. package/dist/provider.js +0 -26
  126. package/dist/pulls.d.ts +0 -2
  127. package/dist/pulls.js +0 -9
  128. package/dist/release-adapters.d.ts +3 -0
  129. package/dist/release-adapters.js +15 -5
  130. package/dist/remote-exec.d.ts +3 -2
  131. package/dist/remote-exec.js +3 -2
  132. package/dist/repos.d.ts +0 -5
  133. package/dist/repos.js +2 -10
  134. package/dist/repository-context.d.ts +0 -2
  135. package/dist/repository-context.js +0 -4
  136. package/dist/restart-certification.d.ts +0 -2
  137. package/dist/restart-certification.js +1 -5
  138. package/dist/result-follow-ups.d.ts +1 -4
  139. package/dist/result-follow-ups.js +1 -6
  140. package/dist/result-review.d.ts +0 -2
  141. package/dist/result-review.js +0 -2
  142. package/dist/retention.d.ts +0 -1
  143. package/dist/retention.js +0 -1
  144. package/dist/review-context.d.ts +0 -1
  145. package/dist/review-context.js +1 -12
  146. package/dist/reviewer.d.ts +0 -16
  147. package/dist/reviewer.js +0 -18
  148. package/dist/run-closeout.js +2 -1
  149. package/dist/scope.js +1 -13
  150. package/dist/scout-browser.js +2 -1
  151. package/dist/serve.js +2 -2
  152. package/dist/server/console.css +5 -133
  153. package/dist/server/flows.js +2 -2
  154. package/dist/server/http.d.ts +2 -0
  155. package/dist/server/http.js +5 -0
  156. package/dist/server/pages.js +2 -2
  157. package/dist/server/people-tokens.js +3 -4
  158. package/dist/server/remote.js +1 -6
  159. package/dist/server/render-chat.d.ts +9 -2
  160. package/dist/server/render-chat.js +53 -7
  161. package/dist/server/render-results.d.ts +0 -6
  162. package/dist/server/render-results.js +3 -9
  163. package/dist/server/render-tasks.js +4 -5
  164. package/dist/server/route-table.d.ts +0 -4
  165. package/dist/server/route-table.js +0 -6
  166. package/dist/server/settings.js +23 -23
  167. package/dist/server/task-views.js +1 -1
  168. package/dist/server/tasks.js +3 -3
  169. package/dist/setup-guide.d.ts +2 -2
  170. package/dist/skills.d.ts +0 -6
  171. package/dist/skills.js +0 -8
  172. package/dist/slack-chat.d.ts +1 -7
  173. package/dist/slack-chat.js +5 -18
  174. package/dist/slack-settings.d.ts +2 -6
  175. package/dist/slack-settings.js +10 -38
  176. package/dist/slack.js +12 -23
  177. package/dist/sleep.d.ts +2 -0
  178. package/dist/sleep.js +2 -0
  179. package/dist/store.d.ts +0 -73
  180. package/dist/store.js +3 -177
  181. package/dist/structured-output.d.ts +0 -1
  182. package/dist/structured-output.js +0 -1
  183. package/dist/subagent-work.d.ts +0 -2
  184. package/dist/subagent-work.js +0 -2
  185. package/dist/subagents.d.ts +1 -3
  186. package/dist/subagents.js +20 -33
  187. package/dist/subscription-chat.d.ts +1 -3
  188. package/dist/subscription-chat.js +2 -3
  189. package/dist/task-sizing.d.ts +1 -4
  190. package/dist/task-sizing.js +3 -10
  191. package/dist/teams-chat.js +4 -11
  192. package/dist/teams.js +12 -19
  193. package/dist/telegram-lead.js +2 -2
  194. package/dist/telegram.js +1 -1
  195. package/dist/token-notices.d.ts +0 -11
  196. package/dist/token-notices.js +1 -5
  197. package/dist/tool-secret-store.d.ts +0 -3
  198. package/dist/tool-secret-store.js +2 -11
  199. package/dist/toolroll-update.d.ts +0 -2
  200. package/dist/toolroll-update.js +2 -2
  201. package/dist/verification-evidence.d.ts +0 -1
  202. package/dist/verification-evidence.js +0 -1
  203. package/dist/webhooks.d.ts +0 -1
  204. package/dist/webhooks.js +0 -1
  205. package/dist/workspace-ui.d.ts +0 -8
  206. package/dist/workspace-ui.js +1 -6
  207. package/package.json +1 -1
  208. package/dist/contracts/native-census.d.ts +0 -42
  209. package/dist/contracts/native-census.js +0 -36
  210. package/dist/process-recovery-anchor.d.ts +0 -75
  211. package/dist/process-recovery-anchor.js +0 -188
  212. package/dist/process-recovery-coalition.d.ts +0 -25
  213. package/dist/process-recovery-coalition.js +0 -105
  214. package/dist/process-recovery-eligibility.d.ts +0 -179
  215. package/dist/process-recovery-eligibility.js +0 -174
  216. package/dist/process-recovery-native.d.ts +0 -161
  217. package/dist/process-recovery-native.js +0 -595
  218. package/dist/process-recovery-provenance.d.ts +0 -163
  219. package/dist/process-recovery-provenance.js +0 -292
  220. package/dist/process-recovery-services.d.ts +0 -56
  221. package/dist/process-recovery-services.js +0 -191
  222. package/dist/process-recovery-settlement.d.ts +0 -32
  223. package/dist/process-recovery-settlement.js +0 -133
  224. package/dist/process-recovery.d.ts +0 -41
  225. package/dist/process-recovery.js +0 -91
  226. package/dist/session-brief.d.ts +0 -8
  227. package/dist/session-brief.js +0 -24
  228. package/dist/session-cli.d.ts +0 -42
  229. package/dist/session-cli.js +0 -271
  230. package/dist/session-contract.d.ts +0 -183
  231. package/dist/session-contract.js +0 -122
  232. package/dist/session-remote.d.ts +0 -19
  233. package/dist/session-remote.js +0 -86
  234. package/dist/session-service.d.ts +0 -22
  235. package/dist/session-service.js +0 -157
  236. package/dist/team-cli.js +0 -692
@@ -15,15 +15,14 @@
15
15
  * The task text is data to the classifier, never instructions, and all it
16
16
  * can return is one of three sizes, a yes/no, and a short reason.
17
17
  */
18
- import { Buffer } from "node:buffer";
19
18
  import { randomUUID } from "node:crypto";
20
19
  import { mkdtempSync, rmSync } from "node:fs";
21
20
  import { tmpdir } from "node:os";
22
21
  import { join } from "node:path";
23
22
  import { readSizeAnswer, SIZING_MODEL_SCHEMA } from "./contracts/task-sizing.js";
24
- import { strictJsonParse } from "./converse.js";
25
23
  import { redactSecretLines, scanForSecrets } from "./evidence.js";
26
24
  import { run, terminateOwnedProcesses } from "./exec.js";
25
+ import { CLAUDE_NO_TOOLS_ARGS, claudeJsonBody } from "./claude-print.js";
27
26
  import { JEV_MODEL, JEV_URL } from "./flow-sort.js";
28
27
  import { readProviderKey } from "./keys.js";
29
28
  import {} from "./phase-routing.js";
@@ -93,13 +92,11 @@ export function claudeSizer(runner = run, model = SIZING_CLAUDE_MODEL, budgetMs
93
92
  sizingText(input),
94
93
  ].join("\n");
95
94
  const result = await runner("claude", [
96
- "-p", "--output-format", "json", "--json-schema", JSON.stringify(SIZING_MODEL_SCHEMA), "--safe-mode", "--no-session-persistence", "--tools", "", "--permission-mode", "dontAsk",
97
- "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--model", model,
95
+ "-p", "--output-format", "json", "--json-schema", JSON.stringify(SIZING_MODEL_SCHEMA), ...CLAUDE_NO_TOOLS_ARGS, "--model", model,
98
96
  ], { cwd: dir, stdin: prompt, timeoutMs: budgetMs, maxBuffer: 256 * 1024, omitEnv: ALL_CREDENTIAL_ENV, processGroup: true, owner, beforeSpawn: () => !signal.aborted });
99
97
  if (signal.aborted || result.notFound || result.timedOut || result.code !== 0)
100
98
  return null;
101
- const parsed = strictJsonParse(Buffer.from(result.stdout, "utf8"), 256 * 1024, 12);
102
- const body = parsed.ok && typeof parsed.value === "object" && parsed.value !== null && !Array.isArray(parsed.value) ? parsed.value : null;
99
+ const body = claudeJsonBody(result.stdout, 256 * 1024);
103
100
  if (body === null || body["is_error"] === true || body["subtype"] !== "success")
104
101
  return null;
105
102
  return answerOf(body["structured_output"]);
@@ -184,10 +181,6 @@ export function installFilingSizer(sizer, budgetMs = SIZING_BUDGET_MS) {
184
181
  return () => { installed = previous; budget = previousBudget; };
185
182
  }
186
183
  let budget = SIZING_BUDGET_MS;
187
- /** Whether filings in this process are classified. */
188
- export function filingSizerInstalled() {
189
- return installed !== null;
190
- }
191
184
  /**
192
185
  * After a filing: ask the classifier (within the budget) and apply its size
193
186
  * to the still-unapproved task. Runs in the background; the filing already
@@ -6,12 +6,11 @@ import { chatAskButtons } from "./chat-ask.js";
6
6
  import { ChatDeliveryError, ChatState, chatHash, partContent } from "./chat-delivery-state.js";
7
7
  import { channelAccess, chatObject as object, heldForWatch, planChatNotifications, planRoomMessages, processChatEvent, splitChatText } from "./chat-delivery.js";
8
8
  import { PLATFORM_LIMITS } from "./text-limits.js";
9
- import { roomCommand } from "./chat-rooms.js";
10
9
  import { LEAD_MESSAGE_MAX_CHARS } from "./lead.js";
11
10
  import { renderReply } from "./reply-shape.js";
12
11
  import { armedCardText, armedYesLabel, proposalLink, proposalOutcomeText, proposalPreview } from "./chat-channel.js";
13
12
  import { chatFlowButtons } from "./chat-flow.js";
14
- import { channelInbox } from "./chat-inbox.js";
13
+ import { admitChatEvent } from "./chat-admission.js";
15
14
  import { chatResultHref } from "./chat-controls.js";
16
15
  import { TeamsError } from "./teams-api.js";
17
16
  import { readTeamsSubmit } from "./contracts/teams-callback.js";
@@ -113,16 +112,10 @@ export function receiveTeams(state, identity, raw, serviceUrlClaim, now) {
113
112
  ...(Array.isArray(activity.attachments) && activity.attachments.length > 0 ? { unsupported: "Incoming files are not supported yet. Describe the request in a message; saved result screenshots open from their links." } : {}) };
114
113
  eventId = chatHash(`${identity.installation}:message:${id}`);
115
114
  }
116
- const words = "text" in payload ? payload.text ?? "" : "";
117
- // v89: a channel that feeds a flow takes anyone's message (as a card, with no say over anything); "flow 12" connects one.
118
- const inbox = isRoom && kind === "message" ? channelInbox(state.store, "teams", identity.installation, channel, words) : { watched: false, command: false };
119
- const roomish = isRoom && (state.room(identity.installation, channel) !== null || (kind === "message" && roomCommand(words) !== null) || inbox.watched || inbox.command);
120
- if (isRoom && !roomish)
121
- return false;
122
- const binding = state.bindingFor(identity.installation, member);
123
- const open = inbox.watched && !inbox.command;
124
- if (kind === "pair" ? !!binding || isRoom : !open && (!binding || !state.live(binding) || (binding.channel !== channel && !roomish)))
115
+ const admitted = admitChatEvent(state, "teams", identity.installation, { kind, member, channel, isRoom, payload });
116
+ if (admitted === null)
125
117
  return false;
118
+ const { binding } = admitted;
126
119
  return state.store.transact(() => {
127
120
  state.setMeta(identity.installation, serviceKey(channel), activity.serviceUrl, now);
128
121
  return state.enqueue({
package/dist/teams.js CHANGED
@@ -4,7 +4,7 @@ import { adapterPolicy } from "./server/route-policy.js";
4
4
  * has no client-initiated socket, so inbound activities arrive over HTTPS
5
5
  * and everything else runs from the database like the other channels. */
6
6
  import { randomBytes } from "node:crypto";
7
- import { setTimeout as sleep } from "node:timers/promises";
7
+ import { abortablePause, workChatQueue } from "./chat-follow.js";
8
8
  import { ChatState } from "./chat-delivery-state.js";
9
9
  import { chatObject as object } from "./chat-delivery.js";
10
10
  import { loadTeamsCredentials, teamsAccessToken, teamsApi, verifyTeamsToken, TeamsError } from "./teams-api.js";
@@ -78,10 +78,7 @@ export async function handleTeamsHttp(request, response, options) {
78
78
  /** The worker-side loop: a lease per installation, one pass a second. */
79
79
  export async function followTeams(options) {
80
80
  const owner = randomBytes(16).toString("hex"), state = new ChatState(options.store, "teams");
81
- const pause = async (ms) => { try {
82
- await sleep(ms, undefined, { signal: options.signal });
83
- }
84
- catch { /* shutdown */ } };
81
+ const pause = abortablePause(options.signal);
85
82
  while (!options.signal.aborted) {
86
83
  const credentials = loadTeamsCredentials(options.dir);
87
84
  if (!credentials || !state.lease(credentials.installation, owner, new Date())) {
@@ -110,20 +107,16 @@ export async function followTeams(options) {
110
107
  await pause(30_000);
111
108
  continue;
112
109
  }
113
- while (same()) {
114
- if (state.retryAt(credentials.installation) > new Date().toISOString()) {
115
- await pause(1000);
116
- continue;
117
- }
118
- await processTeamsEvent(chat);
119
- if (same())
120
- await planTeamsRooms(chat);
121
- if (same())
122
- await deliverTeamsPart(chat);
123
- if (same() && options.notifications())
124
- await planTeamsNotifications(chat);
125
- await pause(1000);
126
- }
110
+ await workChatQueue({
111
+ same,
112
+ ready: () => !(state.retryAt(credentials.installation) > new Date().toISOString()),
113
+ pause,
114
+ notifications: () => options.notifications(),
115
+ process: () => processTeamsEvent(chat),
116
+ rooms: () => planTeamsRooms(chat),
117
+ deliver: () => deliverTeamsPart(chat),
118
+ notify: () => planTeamsNotifications(chat),
119
+ });
127
120
  }
128
121
  catch {
129
122
  if (alive)
@@ -1,5 +1,5 @@
1
1
  import { CHAT_ACTIONS, sharedActionAllowsChallenge, sharedActionPayload } from "./chat-actions.js";
2
- import { taskInCeiling, mirrorToTaskChat, couldNotAnswerText, channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, tooLongText, whichTaskText, replyContextFor, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText } from "./chat-channel.js";
2
+ import { taskInCeiling, mirrorToTaskChat, couldNotAnswerText, channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText } from "./chat-channel.js";
3
3
  export { channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, tooLongText, whichTaskText, replyContextFor, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText, CHAT_ACTION_PARITY as TELEGRAM_ACTION_PARITY, parityGaps } from "./chat-channel.js";
4
4
  /**
5
5
  * The paired phone as one more way to use the same assistant.
@@ -23,7 +23,7 @@ export { channelRepos as telegramConversationRepos, resolveChannelLead as resolv
23
23
  * the console and CLI state), and the door re-proves everything again.
24
24
  */
25
25
  import { createHash, randomBytes } from "node:crypto";
26
- import { LEAD_MESSAGE_MAX_CHARS, leadFailureText, runLeadTurn } from "./lead.js";
26
+ import { leadFailureText, runLeadTurn } from "./lead.js";
27
27
  import { confirmLeadProposal, dismissLeadProposal } from "./lead-doors.js";
28
28
  import { ceilingDigestOf, verifyApproverStanding } from "./principal.js";
29
29
  import { LEAD_ASK_OTHER } from "./store.js";
package/dist/telegram.js CHANGED
@@ -27,7 +27,7 @@ import { chmodSync, readFileSync, rmSync, writeFileSync } from "node:fs";
27
27
  import { join } from "node:path";
28
28
  import { closedQuestionWords, validateNote } from "./decision.js";
29
29
  import { isShotsKind, resultShotsFor } from "./result-shots.js";
30
- import { isLifecycleNotification, isTelegramProgressNotification, RESULT_SHOTS_KIND, TELEGRAM_HOLD_REASONS } from "./store.js";
30
+ import { isLifecycleNotification, isTelegramProgressNotification, TELEGRAM_HOLD_REASONS } from "./store.js";
31
31
  import { telegramProgressCard } from "./telegram-progress.js";
32
32
  import { enqueueEveningDigests, finishedView, isTaskFact, joinsBatch, needsPerson, quietCardView } from "./chat-quiet.js";
33
33
  import { BATCH_MS, chatText, chatTitle, factLinkLabel, mentions, nameTelegramBot } from "./chat-voice.js";
@@ -1,10 +1,3 @@
1
- /**
2
- * API token notices (v111): 7 days and 1 day before a token expires, its person is told once each, in their own chats
3
- * and browsers (a personal notification), and the ledger keeps it. Only the nearest notice newly due is sent, so a
4
- * token made with a day left says so once. Replaced and revoked tokens get none. The same pass records the end of each
5
- * rotated token whose overlap has passed; it already signs no one in (tokenLive), so a late pass grants nothing.
6
- */
7
- import { tokenLive } from "./api-tokens.js";
8
1
  import type { Store } from "./store.js";
9
2
  export declare const TOKEN_NOTICE_KIND = "api-token-expiry";
10
3
  export declare function tokenNoticePass(store: Store, now: Date): {
@@ -14,9 +7,5 @@ export declare function tokenNoticePass(store: Store, now: Date): {
14
7
  }[];
15
8
  ended: number;
16
9
  };
17
- /** Whether a token's console card warns that it expires soon (within the first notice's days). */
18
- export declare function expiresSoon(token: Parameters<typeof tokenLive>[0] & {
19
- replacedBy: string | null;
20
- }, now: number): boolean;
21
10
  /** The loop beside the console: a pass a minute. */
22
11
  export declare function startTokenNotices(store: Store, everyMs?: number): () => void;
@@ -4,7 +4,7 @@
4
4
  * token made with a day left says so once. Replaced and revoked tokens get none. The same pass records the end of each
5
5
  * rotated token whose overlap has passed; it already signs no one in (tokenLive), so a late pass grants nothing.
6
6
  */
7
- import { expiryNoticeDue, EXPIRY_NOTICE_DAYS, tokenLive } from "./api-tokens.js";
7
+ import { expiryNoticeDue, EXPIRY_NOTICE_DAYS } from "./api-tokens.js";
8
8
  export const TOKEN_NOTICE_KIND = "api-token-expiry";
9
9
  export function tokenNoticePass(store, now) {
10
10
  const ended = store.endRotatedApiTokens(now);
@@ -32,10 +32,6 @@ export function tokenNoticePass(store, now) {
32
32
  }
33
33
  return { sent, ended };
34
34
  }
35
- /** Whether a token's console card warns that it expires soon (within the first notice's days). */
36
- export function expiresSoon(token, now) {
37
- return expiryNoticeDue(token, now) !== null;
38
- }
39
35
  /** The loop beside the console: a pass a minute. */
40
36
  export function startTokenNotices(store, everyMs = 60_000) {
41
37
  let stopped = false;
@@ -56,7 +56,6 @@ export declare function detectSecretStore(options?: {
56
56
  export declare function chooseSecretStore(store: SecretBackend | "files" | DetectedStore | null): void;
57
57
  /** This computer's store (found once), or null for owner-only files. */
58
58
  export declare function secretBackend(env?: NodeJS.ProcessEnv): SecretBackend | null;
59
- export declare const secretStoreKind: () => SecretStoreKind;
60
59
  /** Where secrets rest, in the words Settings and `toolroll integrations` share; `problem` when Toolroll's folder can't be used. */
61
60
  export declare function secretStorage(): {
62
61
  kind: SecretStoreKind;
@@ -166,5 +165,3 @@ export declare function writeSealed(backend: SecretBackend, folder: SecretFolder
166
165
  export declare function readHeldSecret(state: string, name: string, file: readonly string[]): string | null;
167
166
  /** Keep (or, with null, remove) a held secret: see readHeldSecret. */
168
167
  export declare function writeHeldSecret(state: string, name: string, file: readonly string[], value: string | null): void;
169
- /** Whether a held secret has a value, without reading it where that can be helped. */
170
- export declare function heldSecretSet(state: string, name: string, file: readonly string[]): boolean;
@@ -32,6 +32,7 @@ import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:
32
32
  import { closeSync, constants, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
33
33
  import { dirname, join } from "node:path";
34
34
  import { envValue } from "./names.js";
35
+ import { sleep } from "./sleep.js";
35
36
  export class SecretStoreError extends Error {
36
37
  }
37
38
  /** The words Settings and `toolroll integrations` use for where secrets rest. */
@@ -200,7 +201,6 @@ function currentStore(env) {
200
201
  export function secretBackend(env = process.env) {
201
202
  return currentStore(env).backend;
202
203
  }
203
- export const secretStoreKind = () => secretBackend()?.kind ?? "files";
204
204
  /** Where secrets rest, in the words Settings and `toolroll integrations` share; `problem` when Toolroll's folder can't be used. */
205
205
  export function secretStorage() {
206
206
  const found = currentStore(process.env);
@@ -701,7 +701,7 @@ export async function withSecretLockAsync(folder, name, work, options = {}) {
701
701
  while (!takeLock(lock, options.staleMs ?? 120_000)) {
702
702
  if (Date.now() - started > (options.waitMs ?? 120_000))
703
703
  throw new SecretStoreError("A sign-in is being renewed elsewhere. Try again in a moment.");
704
- await new Promise(resolve => setTimeout(resolve, 25));
704
+ await sleep(25);
705
705
  }
706
706
  try {
707
707
  return await work();
@@ -823,12 +823,3 @@ export function writeHeldSecret(state, name, file, value) {
823
823
  writeSealed(backend, folder, item, value);
824
824
  });
825
825
  }
826
- /** Whether a held secret has a value, without reading it where that can be helped. */
827
- export function heldSecretSet(state, name, file) {
828
- try {
829
- return readHeldSecret(state, name, file) !== null;
830
- }
831
- catch {
832
- return false;
833
- }
834
- }
@@ -162,7 +162,6 @@ export type StartOptions = {
162
162
  /** An older version replaces the current one only when asked for by name. */
163
163
  allowDowngrade?: boolean;
164
164
  };
165
- declare function nextAt(hhmm: string, now: Date): Date;
166
165
  /** Record an update without running it: the console's job resumes exactly this journal id. */
167
166
  export declare function prepareRuntimeUpdate(o: StartOptions, now: Date): RuntimeUpdateJournal | {
168
167
  refused: string;
@@ -257,4 +256,3 @@ export declare function launchRuntimeUpdate(args: {
257
256
  }): Promise<void>;
258
257
  /** The job's last act: its definition goes, so nothing can start it again. Only the definition for this id. */
259
258
  export declare function retireUpdateJob(id: string, home?: string): void;
260
- export declare const nextScheduledAt: typeof nextAt;
@@ -40,6 +40,7 @@ import { orphanKey, updateSafeSchema, V117_RENAMED_COLUMNS, V117_RENAMED_TABLES,
40
40
  import { isNewer, REGISTRY } from "./releases.js";
41
41
  import { markNeverIndex } from "./never-index.js";
42
42
  import { readRuntimeUpdateJournal, RUNTIME_PHASES, RUNTIME_UPDATE_STEPS, stagedStartedAt, updaterStartingOf } from "./contracts/update-journal.js";
43
+ import { sleep } from "./sleep.js";
43
44
  /** Loaded on first use (as backup.ts and store.ts do), so modules that only import this one (the console,
44
45
  * and tests that load it in a browser-like environment) never need `node:sqlite` itself. */
45
46
  function sqlite() {
@@ -1583,7 +1584,7 @@ export function machineSystem(home = homedir(), env = process.env, seams = {}) {
1583
1584
  };
1584
1585
  return {
1585
1586
  now: () => new Date(),
1586
- sleep: ms => new Promise(done => setTimeout(done, ms)),
1587
+ sleep,
1587
1588
  release: async (version) => {
1588
1589
  const body = await json(`${REGISTRY}/${NAME}/${version}`);
1589
1590
  return { version: String(body.version ?? ""), tarball: String(body.dist?.tarball ?? ""), integrity: String(body.dist?.integrity ?? ""), attestations: body.dist?.attestations?.url ?? null };
@@ -1805,4 +1806,3 @@ export function retireUpdateJob(id, home = homedir()) {
1805
1806
  }
1806
1807
  catch { /* none */ }
1807
1808
  }
1808
- export const nextScheduledAt = nextAt;
@@ -2,7 +2,6 @@ import type { Artifact, Store, VerifyCommand } from "./store.js";
2
2
  import type { VerifyCommandFacts } from "./proof.js";
3
3
  import { type AdjudicateResult } from "./proof.js";
4
4
  export declare const VERIFICATION_RECEIPT_CAPTURE = "machine verification receipt v1";
5
- export declare const REVIEW_GATE_NAME = "REVIEW-VERIFICATION.json";
6
5
  export declare const isVerificationReceipt: (a: Artifact) => boolean;
7
6
  export declare function sealVerificationReceipt(store: Store, root: string, runId: number, head: string, command: VerifyCommand, result: VerifyCommandFacts, now: Date, reusedFrom?: {
8
7
  run: number;
@@ -12,7 +12,6 @@ import { LEGACY_GATE_SOURCE, readVerificationView } from "./contracts/verificati
12
12
  import { readCandidateEndpoints, savedInventoryPaths } from "./contracts/diff-stat.js";
13
13
  import { liveQuickCommand, quickVerifyKey, runCheckLevel } from "./check-levels.js";
14
14
  export const VERIFICATION_RECEIPT_CAPTURE = "machine verification receipt v1";
15
- export const REVIEW_GATE_NAME = "REVIEW-VERIFICATION.json";
16
15
  const hash = (value) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
17
16
  const binding = (a) => ({ artifactId: a.id, sha256: a.sha256, bytesStored: a.bytesStored, bytesOriginal: a.bytesOriginal, truncated: a.truncated, redacted: a.redacted, captureStatus: a.captureStatus });
18
17
  export const isVerificationReceipt = (a) => a.kind === "structured-output" && (a.capture === VERIFICATION_RECEIPT_CAPTURE || a.capture === "machine verification reuse v1");
@@ -6,7 +6,6 @@ export type WebhookTarget = {
6
6
  };
7
7
  export declare const LEGACY_WEBHOOK_WARNING = "Legacy webhooks are deprecated. Connect Slack or Discord in Chat settings.";
8
8
  export declare const CONSOLE_URL_ENV = "TOOLROLL_CONSOLE_URL";
9
- export declare const PRIMARY_ENV = "TOOLROLL_MESSAGING_PRIMARY";
10
9
  export type MessagingChannel = "telegram" | "slack" | "discord" | "teams";
11
10
  export declare const MESSAGING_CHANNELS: readonly MessagingChannel[];
12
11
  export declare function isMessagingChannel(value: string): value is MessagingChannel;
package/dist/webhooks.js CHANGED
@@ -17,7 +17,6 @@ const FILE_OF = { slack: "slack-webhook", discord: "discord-webhook" };
17
17
  const POST_TIMEOUT_MS = 10_000;
18
18
  export const LEGACY_WEBHOOK_WARNING = "Legacy webhooks are deprecated. Connect Slack or Discord in Chat settings.";
19
19
  export const CONSOLE_URL_ENV = "TOOLROLL_CONSOLE_URL";
20
- export const PRIMARY_ENV = "TOOLROLL_MESSAGING_PRIMARY";
21
20
  const CONSOLE_FILE = "console-url";
22
21
  const PRIMARY_FILE = "messaging-primary";
23
22
  export const MESSAGING_CHANNELS = ["telegram", "slack", "discord", "teams"];
@@ -261,14 +261,6 @@ export declare function workStatusOf(facts: WorkFacts, resultDisplay?: DisplaySt
261
261
  export declare function workCounts(rows: readonly {
262
262
  views: readonly WorkView[];
263
263
  }[]): Record<WorkView, number>;
264
- /** All's order: rank, then most recently updated. */
265
- export declare function compareWorkRows(a: {
266
- rank: number;
267
- updatedAt: string;
268
- }, b: {
269
- rank: number;
270
- updatedAt: string;
271
- }): number;
272
264
  /** The shell's three primary destinations, from the page's own active
273
265
  * key: every old page keeps its key and lights the destination it now
274
266
  * lives under. */
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { ACCEPT_NEEDS_REASON, FAILED_CHECK, cantAcceptYetOf, evidenceProblemOf } from "./result-acts.js";
20
20
  export { ACCEPT_NEEDS_REASON, cantAcceptYetOf, evidenceProblemOf };
21
- import { GOAL_ASSESSMENT_PENDING, manualReviewOnly, plainReasonWords } from "./proof.js";
21
+ import { manualReviewOnly, plainReasonWords } from "./proof.js";
22
22
  import { OPEN_RESULT, plainReasonOf, stageOfCode, taskStatusOf, workToneOf } from "./task-status.js";
23
23
  import { whenUtc } from "./when-html.js";
24
24
  import { html, postForm } from "./html.js";
@@ -54,7 +54,6 @@ export function reviewFactsOf(retry, reviewerAlive, automaticPending = false) {
54
54
  }
55
55
  /** The tokens a review in flight wears — the dispatch codes, unchanged. */
56
56
  export const REVIEW_TOKENS = new Set(["reviewing", "review-pending", "review-failed", "review-exhausted"]);
57
- const retriesLeft = (count, explicit = true) => `${count}${explicit ? " explicit" : ""} ${count === 1 ? "retry" : "retries"}`;
58
57
  /**
59
58
  * A review that is queued, running, failed with a retry left, or
60
59
  * exhausted is the result's PRIMARY status — the machine is still
@@ -504,10 +503,6 @@ export function workCounts(rows) {
504
503
  counts[view] += 1;
505
504
  return counts;
506
505
  }
507
- /** All's order: rank, then most recently updated. */
508
- export function compareWorkRows(a, b) {
509
- return a.rank - b.rank || (a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0);
510
- }
511
506
  const WORK_KEYS = new Set(["inbox", "board", "queue", "work", "done", "activity", "review", "tasks", "runs", "workbench", "recipes", "ledger"]);
512
507
  const SETTINGS_KEYS = new Set(["fleet", "caps", "people", "mode", "system", "settings"]);
513
508
  export function primaryDestinationOf(active) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolroll",
3
- "version": "0.9.67",
3
+ "version": "0.9.68",
4
4
  "description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
5
5
  "directories": {
6
6
  "doc": "docs"
@@ -1,42 +0,0 @@
1
- /**
2
- * The macOS process census the recovery helper prints (docs/plans/zod-revamp.md, item 19): `schema: 1`, the boot,
3
- * the collector, and one identity per process. The parser in process-recovery-native.ts reads it with these schemas
4
- * and keeps its fail-closed reason codes: a top-level problem is `malformed-native-census`, one in a process row
5
- * `malformed-process-identity`. Duplicate pids and unique ids, the 64-bit bound on ids and completeness run in plain
6
- * code after parsing. Unknown keys are dropped, and each identity is rebuilt in its sealed key order.
7
- */
8
- import { z } from "zod";
9
- export declare const NATIVE_CENSUS_ERRORS: readonly ["kernel-process-table-unreadable", "process-membership-unreadable", "process-identity-unreadable", "boot-identity-unreadable", "coalition-counters-unreadable", "coalition-counters-changed", "coalition-count-mismatch", "anchor-identity-unreadable"];
10
- export declare const darwinProcessIdentitySchema: z.ZodObject<{
11
- pid: z.ZodInt;
12
- ppid: z.ZodNullable<z.ZodInt>;
13
- uid: z.ZodNullable<z.ZodInt>;
14
- birthMs: z.ZodNullable<z.ZodNumber>;
15
- uniqueId: z.ZodNullable<z.ZodString>;
16
- parentUniqueId: z.ZodNullable<z.ZodString>;
17
- traced: z.ZodNullable<z.ZodBoolean>;
18
- executable: z.ZodNullable<z.ZodString>;
19
- originalParentVersion: z.ZodNullable<z.ZodInt>;
20
- pidVersion: z.ZodExactOptional<z.ZodInt>;
21
- }, z.core.$strip>;
22
- export type DarwinProcessIdentity = z.infer<typeof darwinProcessIdentitySchema>;
23
- /** Top level only: process rows are read one by one, so a bad row gets its own reason. */
24
- export declare const darwinNativeCensusSchema: z.ZodObject<{
25
- schema: z.ZodLiteral<1>;
26
- bootId: z.ZodNullable<z.ZodString>;
27
- collectorPid: z.ZodInt;
28
- complete: z.ZodBoolean;
29
- processes: z.ZodArray<z.ZodUnknown>;
30
- errors: z.ZodArray<z.ZodEnum<{
31
- "anchor-identity-unreadable": "anchor-identity-unreadable";
32
- "boot-identity-unreadable": "boot-identity-unreadable";
33
- "coalition-count-mismatch": "coalition-count-mismatch";
34
- "coalition-counters-changed": "coalition-counters-changed";
35
- "coalition-counters-unreadable": "coalition-counters-unreadable";
36
- "kernel-process-table-unreadable": "kernel-process-table-unreadable";
37
- "process-identity-unreadable": "process-identity-unreadable";
38
- "process-membership-unreadable": "process-membership-unreadable";
39
- }>>;
40
- }, z.core.$strip>;
41
- /** A kernel id within 64 bits (the schema checks the digits). */
42
- export declare const kernelIdInRange: (id: string) => boolean;
@@ -1,36 +0,0 @@
1
- /**
2
- * The macOS process census the recovery helper prints (docs/plans/zod-revamp.md, item 19): `schema: 1`, the boot,
3
- * the collector, and one identity per process. The parser in process-recovery-native.ts reads it with these schemas
4
- * and keeps its fail-closed reason codes: a top-level problem is `malformed-native-census`, one in a process row
5
- * `malformed-process-identity`. Duplicate pids and unique ids, the 64-bit bound on ids and completeness run in plain
6
- * code after parsing. Unknown keys are dropped, and each identity is rebuilt in its sealed key order.
7
- */
8
- import { z } from "zod";
9
- export const NATIVE_CENSUS_ERRORS = ["kernel-process-table-unreadable", "process-membership-unreadable", "process-identity-unreadable", "boot-identity-unreadable", "coalition-counters-unreadable", "coalition-counters-changed", "coalition-count-mismatch", "anchor-identity-unreadable"];
10
- /** A kernel unique id: a decimal unsigned 64-bit integer as text (the bound itself is checked after parsing). */
11
- const kernelId = z.string().regex(/^(0|[1-9][0-9]{0,19})$/);
12
- const count = z.int().min(0);
13
- const version32 = z.int().min(0).max(0xffffffff);
14
- export const darwinProcessIdentitySchema = z.object({
15
- pid: z.int().min(1),
16
- ppid: count.nullable(),
17
- uid: count.nullable(),
18
- birthMs: z.number().positive().nullable(),
19
- uniqueId: kernelId.nullable(),
20
- parentUniqueId: kernelId.nullable(),
21
- traced: z.boolean().nullable(),
22
- executable: z.string().max(4096).regex(/^[^\x00-\x1f\x7f]*$/).nullable(),
23
- originalParentVersion: version32.nullable(),
24
- pidVersion: version32.exactOptional(),
25
- });
26
- /** Top level only: process rows are read one by one, so a bad row gets its own reason. */
27
- export const darwinNativeCensusSchema = z.object({
28
- schema: z.literal(1),
29
- bootId: z.string().regex(/^[a-fA-F0-9-]{36}$/).nullable(),
30
- collectorPid: z.int().min(2),
31
- complete: z.boolean(),
32
- processes: z.array(z.unknown()).max(100_000),
33
- errors: z.array(z.enum(NATIVE_CENSUS_ERRORS)),
34
- });
35
- /** A kernel id within 64 bits (the schema checks the digits). */
36
- export const kernelIdInRange = (id) => BigInt(id) <= 0xffffffffffffffffn;
@@ -1,75 +0,0 @@
1
- import type { SQLInputValue } from "node:sqlite";
2
- import type { DarwinCoalitionProcessIdentity, PreparedDarwinProcessCensus } from "./process-recovery-native.js";
3
- /** This version's causal argument was audited against these installed bytes:
4
- * up registers a fresh token BEFORE listening; every watch creates a fresh UUID;
5
- * heartbeat authenticates that closure's token and exact owner transactionally.
6
- * A new runtime needs its own audit, not a caller assertion of equivalent code. */
7
- export declare const SERVICE_ANCHOR_RUNTIME_V1_SHA256 = "9641efcb3b46717a7f122aa025e3945a08fa55ca046605276825a9d69eefd01d";
8
- export type DarwinServiceAnchorInput = {
9
- databasePath: string;
10
- targetRun: number;
11
- anchorReviewerRun: number;
12
- runtime: string;
13
- runtimeSha256: string;
14
- runner: string;
15
- repo: string;
16
- host: string;
17
- bootId: string;
18
- resourceCoalitionId: string;
19
- service: string;
20
- supervisorPid: number;
21
- workerPid: number;
22
- port: number;
23
- /** Full expected arrays; compared privately, never included in a receipt. */
24
- supervisorArgv: string[];
25
- workerArgv: string[];
26
- /** Recovery must prepare this helper itself from the known embedded source;
27
- * never accept an executable or this result receipt from a client request. */
28
- native: PreparedDarwinProcessCensus;
29
- maxWaitMs?: number;
30
- };
31
- export type DarwinServiceAnchorReceipt = {
32
- schema: 1;
33
- kind: "authenticated-watch-continuity";
34
- targetRun: number;
35
- anchorReviewerRun: number;
36
- host: string;
37
- bootId: string;
38
- observedAt: string;
39
- runtimeSha256: string;
40
- databasePath: string;
41
- runner: string;
42
- repo: string;
43
- owner: string;
44
- generation: number;
45
- episode: number;
46
- registeredAt: string;
47
- heartbeatBefore: string;
48
- heartbeatAfter: string;
49
- service: string;
50
- listener: string;
51
- worker: DarwinCoalitionProcessIdentity;
52
- supervisor: DarwinCoalitionProcessIdentity;
53
- nativeSourceSha256: string;
54
- nativeExecutableSha256: string;
55
- };
56
- export type DarwinServiceAnchorResult = {
57
- ok: true;
58
- receipt: DarwinServiceAnchorReceipt;
59
- } | {
60
- ok: false;
61
- reason: string;
62
- };
63
- export type RecoveryReadDatabase = {
64
- prepare(sql: string): {
65
- get(...parameters: SQLInputValue[]): Record<string, unknown> | undefined;
66
- all(...parameters: SQLInputValue[]): Record<string, unknown>[];
67
- };
68
- };
69
- /** Trusted read-only collection, not exit evidence. Reads an existing connection
70
- * (including a schema69 Store.handle), never opens/migrates or writes a database.
71
- * Under the trusted installed-code/credential model, renewing a saved pre-run
72
- * UUID while registration stays fixed establishes a pre-run live service bound.
73
- * It does NOT establish coalition inheritance, survivor exclusion, or authority
74
- * to clear process custody. Every incomplete observation returns a refusal. */
75
- export declare function collectDarwinServiceAnchor(db: RecoveryReadDatabase, input: DarwinServiceAnchorInput): Promise<DarwinServiceAnchorResult>;