@atolis-hq/wake 0.1.4 → 0.1.6

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 (35) hide show
  1. package/README.md +1 -1
  2. package/dist/src/adapters/claude/claude-runner.js +9 -30
  3. package/dist/src/adapters/codex/codex-runner.js +5 -10
  4. package/dist/src/adapters/fake/fake-github-pull-request-activity-source.js +5 -1
  5. package/dist/src/adapters/fake/fake-runner.js +2 -2
  6. package/dist/src/adapters/fake/fake-ticketing-system.js +3 -8
  7. package/dist/src/adapters/fake/fake-workspace-manager.js +1 -1
  8. package/dist/src/adapters/fs/state-store.js +8 -10
  9. package/dist/src/adapters/github/github-auth.js +1 -1
  10. package/dist/src/adapters/github/github-client.js +31 -0
  11. package/dist/src/adapters/github/github-issues-work-source.js +8 -14
  12. package/dist/src/adapters/github/github-pull-request-activity-source.js +162 -9
  13. package/dist/src/adapters/http/ui-data.js +23 -12
  14. package/dist/src/adapters/http/ui-server.js +14 -4
  15. package/dist/src/adapters/runner/runner-registry.js +2 -4
  16. package/dist/src/adapters/runner/stage-prompt.js +15 -18
  17. package/dist/src/cli/sandbox-command.js +8 -6
  18. package/dist/src/cli/startup-preflight.js +1 -1
  19. package/dist/src/core/control-plane.js +6 -2
  20. package/dist/src/core/policy-engine.js +10 -21
  21. package/dist/src/core/projection-updater.js +9 -18
  22. package/dist/src/core/quota-backoff.js +1 -1
  23. package/dist/src/core/sink-router.js +5 -7
  24. package/dist/src/core/tick-runner.js +7 -14
  25. package/dist/src/domain/runner-routing.js +1 -1
  26. package/dist/src/domain/schema.js +214 -56
  27. package/dist/src/domain/stages.js +1 -4
  28. package/dist/src/domain/workflows.js +1 -3
  29. package/dist/src/lib/lock.js +1 -1
  30. package/dist/src/main.js +5 -8
  31. package/dist/src/version.js +1 -1
  32. package/package.json +10 -2
  33. package/prompts/implement.md +4 -0
  34. package/prompts/refine.md +2 -0
  35. package/prompts/revise.md +4 -3
@@ -145,7 +145,11 @@ export async function buildStatus(input) {
145
145
  ? { level: 'unknown' }
146
146
  : {
147
147
  ageMs: worstAgeMs,
148
- level: worstAgeMs > intervalMs * 10 ? 'red' : worstAgeMs > intervalMs * 3 ? 'amber' : 'ok',
148
+ level: worstAgeMs > intervalMs * 10
149
+ ? 'red'
150
+ : worstAgeMs > intervalMs * 3
151
+ ? 'amber'
152
+ : 'ok',
149
153
  };
150
154
  return {
151
155
  loopState,
@@ -154,7 +158,11 @@ export async function buildStatus(input) {
154
158
  lock,
155
159
  lastEvent: lastEvent === undefined
156
160
  ? undefined
157
- : { at: lastEvent.ingestedAt, type: lastEvent.sourceEventType, workItemKey: lastEvent.workItemKey },
161
+ : {
162
+ at: lastEvent.ingestedAt,
163
+ type: lastEvent.sourceEventType,
164
+ workItemKey: lastEvent.workItemKey,
165
+ },
158
166
  lastRun: lastRun === undefined
159
167
  ? undefined
160
168
  : {
@@ -338,8 +346,15 @@ export async function buildHealth(input) {
338
346
  const integrityChecks = await Promise.all(items
339
347
  .filter((item) => item.wake.workspacePath !== undefined)
340
348
  .map(async (item) => {
341
- const exists = await stat(item.wake.workspacePath).then(() => true).catch(() => false);
342
- return exists ? null : { path: item.wake.workspacePath, problem: `workspacePath missing for ${item.workItemKey}` };
349
+ const exists = await stat(item.wake.workspacePath)
350
+ .then(() => true)
351
+ .catch(() => false);
352
+ return exists
353
+ ? null
354
+ : {
355
+ path: item.wake.workspacePath,
356
+ problem: `workspacePath missing for ${item.workItemKey}`,
357
+ };
343
358
  }));
344
359
  const integrityIssues = integrityChecks.filter((issue) => issue !== null);
345
360
  return {
@@ -359,16 +374,12 @@ export async function buildHealth(input) {
359
374
  }
360
375
  export async function buildWorkspaces(input) {
361
376
  const items = await input.stateStore.listIssueStates();
362
- const byWorkspacePath = new Map(items.filter((item) => item.wake.workspacePath !== undefined).map((item) => [item.wake.workspacePath, item]));
377
+ const byWorkspacePath = new Map(items
378
+ .filter((item) => item.wake.workspacePath !== undefined)
379
+ .map((item) => [item.wake.workspacePath, item]));
363
380
  const workspaceRoot = input.stateStore.paths.workspaceRoot;
364
381
  const workspaces = [];
365
- let repoDirs = [];
366
- try {
367
- repoDirs = await readdir(workspaceRoot);
368
- }
369
- catch {
370
- repoDirs = [];
371
- }
382
+ const repoDirs = await readdir(workspaceRoot).catch(() => []);
372
383
  for (const repoDir of repoDirs) {
373
384
  const repoPath = join(workspaceRoot, repoDir);
374
385
  const issueDirs = await readdir(repoPath).catch(() => []);
@@ -17,7 +17,10 @@ function extractBearerToken(req) {
17
17
  }
18
18
  const cookie = req.headers.cookie;
19
19
  if (typeof cookie === 'string') {
20
- const match = cookie.split(';').map((part) => part.trim()).find((part) => part.startsWith('wake_ui_token='));
20
+ const match = cookie
21
+ .split(';')
22
+ .map((part) => part.trim())
23
+ .find((part) => part.startsWith('wake_ui_token='));
21
24
  if (match !== undefined) {
22
25
  return match.slice('wake_ui_token='.length);
23
26
  }
@@ -75,11 +78,17 @@ async function handleRequest(req, res, options, now) {
75
78
  return;
76
79
  }
77
80
  if (req.method !== 'GET') {
78
- sendJson(res, 405, { error: 'this build only serves read endpoints; mutations are not implemented' });
81
+ sendJson(res, 405, {
82
+ error: 'this build only serves read endpoints; mutations are not implemented',
83
+ });
79
84
  return;
80
85
  }
81
86
  const { stateStore, resourceIndex, config } = options;
82
- const segments = url.pathname.slice('/api/v1/'.length).split('/').filter((part) => part.length > 0).map((s) => decodeURIComponent(s));
87
+ const segments = url.pathname
88
+ .slice('/api/v1/'.length)
89
+ .split('/')
90
+ .filter((part) => part.length > 0)
91
+ .map((s) => decodeURIComponent(s));
83
92
  const resource = segments[0];
84
93
  if (resource === 'status' && segments.length === 1) {
85
94
  sendJson(res, 200, await buildStatus({ stateStore, config, now: now() }));
@@ -129,7 +138,8 @@ async function handleRequest(req, res, options, now) {
129
138
  sendJson(res, 200, await buildEventsFeed({
130
139
  stateStore,
131
140
  workItemKey: url.searchParams.get('workItemKey') ?? undefined,
132
- direction: url.searchParams.get('direction') ?? undefined,
141
+ direction: url.searchParams.get('direction') ??
142
+ undefined,
133
143
  type: url.searchParams.get('type') ?? undefined,
134
144
  limit: limitParam === null ? undefined : Number(limitParam),
135
145
  }));
@@ -3,7 +3,7 @@ import { createCodexRunner } from '../codex/codex-runner.js';
3
3
  import { createCursorRunner } from '../cursor/cursor-runner.js';
4
4
  import { createFakeRunner } from '../fake/fake-runner.js';
5
5
  import { resolveRunnerRouting } from '../../domain/runner-routing.js';
6
- import { chooseAction, workflowForProjection, workflowNameForProjection } from '../../domain/workflows.js';
6
+ import { chooseAction, workflowForProjection, workflowNameForProjection, } from '../../domain/workflows.js';
7
7
  export { resolveRunnerRouting } from '../../domain/runner-routing.js';
8
8
  function withoutKind(entry) {
9
9
  const { kind: _kind, ...settings } = entry;
@@ -102,9 +102,7 @@ export function createRegistryRunner(input) {
102
102
  const routing = runInput.routing ??
103
103
  (() => {
104
104
  const workflow = workflowForProjection(runInput.projection, runInput.config);
105
- const workflowAction = workflow === null
106
- ? null
107
- : chooseAction(runInput.projection, workflow);
105
+ const workflowAction = workflow === null ? null : chooseAction(runInput.projection, workflow);
108
106
  return resolveRunnerRouting({
109
107
  config: runInput.config,
110
108
  stage: workflowAction?.stage ?? runInput.projection.wake.stage,
@@ -52,9 +52,7 @@ function previousCommentsThroughLastRun(projection) {
52
52
  return cursorIndex === -1 ? [] : projection.comments.slice(0, cursorIndex + 1);
53
53
  }
54
54
  function matchesCommand(body, pattern) {
55
- return body
56
- .split(/\r?\n/)
57
- .some((line) => pattern.test(line.trim()));
55
+ return body.split(/\r?\n/).some((line) => pattern.test(line.trim()));
58
56
  }
59
57
  function latestQuestionCommandNote(input) {
60
58
  const { comments, successSentinel } = input;
@@ -147,10 +145,7 @@ function buildUntrustedDataBlock(input) {
147
145
  '',
148
146
  'Issue:',
149
147
  ...(input.includeRepoDetails
150
- ? [
151
- `- Repo: ${input.projection.issue.repo}`,
152
- `- Number: ${input.projection.issue.number}`,
153
- ]
148
+ ? [`- Repo: ${input.projection.issue.repo}`, `- Number: ${input.projection.issue.number}`]
154
149
  : []),
155
150
  `- Title: ${input.projection.issue.title}`,
156
151
  `- Stage: ${input.projection.wake.stage}`,
@@ -196,12 +191,10 @@ export async function buildStagePrompt(input) {
196
191
  // Default tool capability note — runner adapters can override this via contextOverrides
197
192
  // when the runner's tool model differs from Claude Code's named-tool model (e.g. Codex).
198
193
  if (mode === 'resume') {
199
- context.toolCapabilityNote =
200
- `Reminder: this is still a planning-only stage - your only available tools are: ${allowedToolsListStr}. Do not attempt to use Edit, Write, or any Bash command other than the git commands listed above, or modify any file.`;
194
+ context.toolCapabilityNote = `Reminder: this is still a planning-only stage - your only available tools are: ${allowedToolsListStr}. Do not attempt to use Edit, Write, or any Bash command other than the git commands listed above, or modify any file.`;
201
195
  }
202
196
  else {
203
- context.toolCapabilityNote =
204
- `Your only available tools are: ${allowedToolsListStr}.\nDo not attempt to use Edit, Write, or any Bash command other than the git commands listed above unless this stage's prompt and workspace mode explicitly allow it.`;
197
+ context.toolCapabilityNote = `Your only available tools are: ${allowedToolsListStr}.\nDo not attempt to use Edit, Write, or any Bash command other than the git commands listed above unless this stage's prompt and workspace mode explicitly allow it.`;
205
198
  }
206
199
  if (input.contextOverrides !== undefined) {
207
200
  Object.assign(context, input.contextOverrides);
@@ -210,12 +203,13 @@ export async function buildStagePrompt(input) {
210
203
  const permissionMode = template.frontmatter.permissionMode;
211
204
  const commentsToAddress = newCommentsSinceLastRun(input.projection);
212
205
  const priorComments = previousCommentsThroughLastRun(input.projection);
213
- context.feedbackCommandNote = mode === 'resume'
214
- ? latestQuestionCommandNote({
215
- comments: commentsToAddress,
216
- successSentinel: skipApproval ? 'DONE' : 'AWAITING_APPROVAL',
217
- })
218
- : '';
206
+ context.feedbackCommandNote =
207
+ mode === 'resume'
208
+ ? latestQuestionCommandNote({
209
+ comments: commentsToAddress,
210
+ successSentinel: skipApproval ? 'DONE' : 'AWAITING_APPROVAL',
211
+ })
212
+ : '';
219
213
  const commentSections = mode === 'resume'
220
214
  ? [
221
215
  {
@@ -261,7 +255,10 @@ export async function buildStagePrompt(input) {
261
255
  }),
262
256
  allowedTools,
263
257
  extraArgs: parseFrontmatterArgs(template.frontmatter.extraArgs),
264
- maxTurns: parseFrontmatterMaxTurns({ action: input.action, value: template.frontmatter.maxTurns }),
258
+ maxTurns: parseFrontmatterMaxTurns({
259
+ action: input.action,
260
+ value: template.frontmatter.maxTurns,
261
+ }),
265
262
  skipApproval,
266
263
  ...(permissionMode === undefined ? {} : { permissionMode }),
267
264
  };
@@ -4,13 +4,11 @@ import { createRunnerCliAdapter } from '../adapters/runner/runner-cli-adapter.js
4
4
  import { runSandboxResumeCommand } from './sandbox-resume.js';
5
5
  import { runSelfUpdateCommand, runSelfUpdateLoop } from './self-update-command.js';
6
6
  import { runStopCommand } from './stop-command.js';
7
- import { buildSandboxLoggedCommand, } from './sandbox-logging.js';
7
+ import { buildSandboxLoggedCommand } from './sandbox-logging.js';
8
8
  async function ensureContainerHomeMountParents(input) {
9
9
  for (const mount of input.extraMounts) {
10
10
  const relativeTarget = posix.relative(input.containerHomeMountPath, mount.target);
11
- if (relativeTarget.length === 0 ||
12
- relativeTarget === '.' ||
13
- relativeTarget.startsWith('..')) {
11
+ if (relativeTarget.length === 0 || relativeTarget === '.' || relativeTarget.startsWith('..')) {
14
12
  continue;
15
13
  }
16
14
  const parentRelativeTarget = posix.dirname(relativeTarget);
@@ -118,7 +116,9 @@ export async function runSandboxCommand(input) {
118
116
  throw new Error('Sandbox self-update requires git/issueReporter/ledger dependencies');
119
117
  }
120
118
  const selfUpdateArgs = input.args.slice(1);
121
- const runSelfUpdate = selfUpdateArgs.includes('--loop') ? runSelfUpdateLoop : runSelfUpdateCommand;
119
+ const runSelfUpdate = selfUpdateArgs.includes('--loop')
120
+ ? runSelfUpdateLoop
121
+ : runSelfUpdateCommand;
122
122
  await runSelfUpdate({
123
123
  args: selfUpdateArgs,
124
124
  repoRoot,
@@ -146,7 +146,9 @@ export async function runSandboxCommand(input) {
146
146
  return;
147
147
  }
148
148
  if (subcommand === 'setup') {
149
- await input.docker.exec(input.config.sandbox.containerName, ['bash', '/wake/docker/setup.sh'], { interactive: true });
149
+ await input.docker.exec(input.config.sandbox.containerName, ['bash', '/wake/docker/setup.sh'], {
150
+ interactive: true,
151
+ });
150
152
  return;
151
153
  }
152
154
  if (subcommand === 'exec') {
@@ -52,7 +52,7 @@ async function defaultCheckRunnerCommand(runnerName, entry) {
52
52
  }
53
53
  catch (error) {
54
54
  const message = error instanceof Error ? error.message : String(error);
55
- throw new Error(`runner "${runnerName}" (${entry.kind}) command "${entry.command}" is not invocable: ${message}`);
55
+ throw new Error(`runner "${runnerName}" (${entry.kind}) command "${entry.command}" is not invocable: ${message}`, { cause: error });
56
56
  }
57
57
  }
58
58
  async function assertPromptsRootAccessible(promptsRoot) {
@@ -91,13 +91,17 @@ export function createControlPlane(deps) {
91
91
  run: deps.tickRunner.runIntakeTick,
92
92
  label: 'intake',
93
93
  getIdleTicks: () => consecutiveIntakeIdleTicks,
94
- setIdleTicks: (value) => { consecutiveIntakeIdleTicks = value; },
94
+ setIdleTicks: (value) => {
95
+ consecutiveIntakeIdleTicks = value;
96
+ },
95
97
  }),
96
98
  startLoop({
97
99
  run: deps.tickRunner.runRunnerTick,
98
100
  label: 'runner',
99
101
  getIdleTicks: () => consecutiveRunnerIdleTicks,
100
- setIdleTicks: (value) => { consecutiveRunnerIdleTicks = value; },
102
+ setIdleTicks: (value) => {
103
+ consecutiveRunnerIdleTicks = value;
104
+ },
101
105
  }),
102
106
  ]);
103
107
  return;
@@ -1,5 +1,5 @@
1
- import { awaitingApprovalRunnerSentinel, failedRunnerSentinel, } from '../domain/stages.js';
2
- import { builtInDefaultWorkflowDefinition, chooseAction as chooseWorkflowAction } from '../domain/workflows.js';
1
+ import { awaitingApprovalRunnerSentinel, failedRunnerSentinel } from '../domain/stages.js';
2
+ import { builtInDefaultWorkflowDefinition, chooseAction as chooseWorkflowAction, } from '../domain/workflows.js';
3
3
  function isAwaitingApproval(issue) {
4
4
  const context = issue.context;
5
5
  return context.lastRunSentinel === awaitingApprovalRunnerSentinel;
@@ -15,9 +15,7 @@ const questionCommandPattern = /^\/question\b/i;
15
15
  // lateral response to a PR surface, not a workflow stage.
16
16
  const reviewFeedbackAction = 'revise';
17
17
  function matchesCommand(body, pattern) {
18
- return body
19
- .split(/\r?\n/)
20
- .some((line) => pattern.test(line.trim()));
18
+ return body.split(/\r?\n/).some((line) => pattern.test(line.trim()));
21
19
  }
22
20
  function labelsAndAssigneesQualify(input) {
23
21
  if (input.requiredLabels.length === 0 && input.requiredAssignees.length === 0) {
@@ -31,16 +29,15 @@ function labelsAndAssigneesQualify(input) {
31
29
  if (input.ignoredLabels.some((label) => labels.has(label))) {
32
30
  return false;
33
31
  }
34
- if (input.requiredAssignees.length > 0 && !input.requiredAssignees.some((login) => assignees.has(login))) {
32
+ if (input.requiredAssignees.length > 0 &&
33
+ !input.requiredAssignees.some((login) => assignees.has(login))) {
35
34
  return false;
36
35
  }
37
36
  return true;
38
37
  }
39
38
  function latestUnhandledHumanComment(issue) {
40
39
  const context = issue.context;
41
- const handledCommentId = typeof context.lastHandledCommentId === 'string'
42
- ? context.lastHandledCommentId
43
- : undefined;
40
+ const handledCommentId = typeof context.lastHandledCommentId === 'string' ? context.lastHandledCommentId : undefined;
44
41
  // Only consider human comments that appear after the last bot comment.
45
42
  // A human /approved posted before Wake's approval-request comment must not
46
43
  // be re-consumed as approval for a later awaiting-approval cycle.
@@ -78,18 +75,10 @@ export function createPolicyEngine() {
78
75
  },
79
76
  needsWakeAction(issue, workflow = builtInDefaultWorkflowDefinition) {
80
77
  const context = issue.context;
81
- const handledCommentId = typeof context.lastHandledCommentId === 'string'
82
- ? context.lastHandledCommentId
83
- : undefined;
84
- const lastCompletedAction = typeof context.lastCompletedAction === 'string'
85
- ? context.lastCompletedAction
86
- : undefined;
87
- const lastRunSentinel = typeof context.lastRunSentinel === 'string'
88
- ? context.lastRunSentinel
89
- : undefined;
90
- const lastFailureClass = typeof context.lastFailureClass === 'string'
91
- ? context.lastFailureClass
92
- : undefined;
78
+ const handledCommentId = typeof context.lastHandledCommentId === 'string' ? context.lastHandledCommentId : undefined;
79
+ const lastCompletedAction = typeof context.lastCompletedAction === 'string' ? context.lastCompletedAction : undefined;
80
+ const lastRunSentinel = typeof context.lastRunSentinel === 'string' ? context.lastRunSentinel : undefined;
81
+ const lastFailureClass = typeof context.lastFailureClass === 'string' ? context.lastFailureClass : undefined;
93
82
  if (issue.wake.lastRunId === undefined) {
94
83
  return true;
95
84
  }
@@ -15,14 +15,12 @@ function configuredStagesForLabels(config) {
15
15
  return workflow === undefined ? undefined : workflowStageVocabulary(workflow);
16
16
  }
17
17
  function createProjectionFromIssueEvent(event, config) {
18
- const issue = event.sourceEventType === 'ticket.upsert'
19
- ? event.payload.ticket
20
- : event.payload.issue;
18
+ const issue = event.sourceEventType === 'ticket.upsert' ? event.payload.ticket : event.payload.issue;
21
19
  if (issue === undefined || typeof issue !== 'object' || issue === null) {
22
20
  return null;
23
21
  }
24
22
  const labels = Array.isArray(issue.labels)
25
- ? (issue.labels)
23
+ ? issue.labels
26
24
  : [];
27
25
  return parseIssueStateRecord({
28
26
  schemaVersion: 1,
@@ -50,8 +48,7 @@ async function applyEvent(current, event, ctx, config) {
50
48
  if (event.workItemKey === UNRESOLVED_WORK_ITEM_KEY) {
51
49
  return current;
52
50
  }
53
- if (event.sourceEventType === 'fake.issue.upsert' ||
54
- event.sourceEventType === 'ticket.upsert') {
51
+ if (event.sourceEventType === 'fake.issue.upsert' || event.sourceEventType === 'ticket.upsert') {
55
52
  const next = createProjectionFromIssueEvent(event, config);
56
53
  if (next === null) {
57
54
  return current;
@@ -91,7 +88,8 @@ async function applyEvent(current, event, ctx, config) {
91
88
  event.sourceEventType === 'ticket.comment.updated' ||
92
89
  event.sourceEventType === 'pr.comment.created' ||
93
90
  event.sourceEventType === 'pr.review.created' ||
94
- event.sourceEventType === 'pr.review-comment.created') {
91
+ event.sourceEventType === 'pr.review-comment.created' ||
92
+ event.sourceEventType === 'pr.checks.failed') {
95
93
  const comment = event.payload.comment;
96
94
  if (comment === undefined || typeof comment !== 'object' || comment === null) {
97
95
  return current;
@@ -149,8 +147,7 @@ async function applyEvent(current, event, ctx, config) {
149
147
  // Clear the session when the stage moves forward (new action needed) or the
150
148
  // run failed outright. Keep it for BLOCKED so the same action can resume
151
149
  // the in-progress session after a human replies.
152
- const isForwardProgression = payload.nextStage !== undefined &&
153
- payload.nextStage !== current.wake.stage;
150
+ const isForwardProgression = payload.nextStage !== undefined && payload.nextStage !== current.wake.stage;
154
151
  const stageChanged = payload.nextStage !== undefined && payload.nextStage !== current.wake.stage;
155
152
  const isFailed = payload.sentinel === 'FAILED';
156
153
  const shouldClearSession = isForwardProgression || isFailed;
@@ -162,12 +159,8 @@ async function applyEvent(current, event, ctx, config) {
162
159
  ...(payload.handledCommentId === undefined
163
160
  ? {}
164
161
  : { lastHandledCommentId: payload.handledCommentId }),
165
- ...(payload.sentinel === undefined
166
- ? {}
167
- : { lastRunSentinel: payload.sentinel }),
168
- ...(payload.action === undefined
169
- ? {}
170
- : { lastRunAction: payload.action }),
162
+ ...(payload.sentinel === undefined ? {} : { lastRunSentinel: payload.sentinel }),
163
+ ...(payload.action === undefined ? {} : { lastRunAction: payload.action }),
171
164
  ...(payload.sentinel === doneRunnerSentinel && payload.action !== undefined
172
165
  ? { lastCompletedAction: payload.action }
173
166
  : {}),
@@ -181,9 +174,7 @@ async function applyEvent(current, event, ctx, config) {
181
174
  ...current.wake,
182
175
  stage: payload.nextStage ?? current.wake.stage,
183
176
  lastRunId: payload.runId ?? current.wake.lastRunId,
184
- sessionId: shouldClearSession
185
- ? undefined
186
- : (payload.sessionId ?? current.wake.sessionId),
177
+ sessionId: shouldClearSession ? undefined : (payload.sessionId ?? current.wake.sessionId),
187
178
  sessionCli: shouldClearSession
188
179
  ? undefined
189
180
  : (payload.sessionCli ?? current.wake.sessionCli),
@@ -61,7 +61,7 @@ export function resolveQuotaPauseUntil(input) {
61
61
  };
62
62
  }
63
63
  const boundedFailureCount = Math.max(1, Math.min(input.failureCount, 6));
64
- const delayMs = Math.min(15 * 60_000 * (2 ** (boundedFailureCount - 1)), maxEstimatedPauseMs);
64
+ const delayMs = Math.min(15 * 60_000 * 2 ** (boundedFailureCount - 1), maxEstimatedPauseMs);
65
65
  return {
66
66
  pausedUntil: new Date(input.now.getTime() + delayMs).toISOString(),
67
67
  source: 'estimated',
@@ -46,18 +46,16 @@ export function createOutboundSinkRouter(input) {
46
46
  const origin = typeof event.payload.origin === 'string' ? event.payload.origin : undefined;
47
47
  const sourceOrigin = event.sourceRefs.sink ?? origin;
48
48
  if (event.sourceEventType === 'wake.labels.requested') {
49
- const projectionOrigin = typeof event.payload.origin === 'string'
50
- ? event.payload.origin
51
- : undefined;
49
+ const projectionOrigin = typeof event.payload.origin === 'string' ? event.payload.origin : undefined;
52
50
  if (projectionOrigin !== undefined) {
53
51
  targetSinks.add(projectionOrigin);
54
52
  }
55
53
  }
56
- const kind = intentKind(event);
57
54
  const resourceUri = event.sourceRefs.resourceUri;
58
- if (event.sourceEventType === 'wake.publish.intent.requested' &&
59
- sourceOrigin !== undefined) {
60
- const resourceSink = resourceUri === undefined ? sourceOrigin : sinkNameForResourceUri(resourceUri, sourceOrigin);
55
+ if (event.sourceEventType === 'wake.publish.intent.requested' && sourceOrigin !== undefined) {
56
+ const resourceSink = resourceUri === undefined
57
+ ? sourceOrigin
58
+ : sinkNameForResourceUri(resourceUri, sourceOrigin);
61
59
  // A resource-derived sink name (e.g. a PR surface) may not be
62
60
  // registered — the source that owns it can be disabled independently
63
61
  // of the origin sink. Falling back to sourceOrigin here, rather than
@@ -169,9 +169,7 @@ export function createTickRunner(deps) {
169
169
  ...(input.runnerResult.tokenUsage?.costUsd === undefined
170
170
  ? {}
171
171
  : { cost: formatCostUsd(input.runnerResult.tokenUsage.costUsd) }),
172
- ...(input.workspacePath === undefined
173
- ? {}
174
- : { workspacePath: input.workspacePath }),
172
+ ...(input.workspacePath === undefined ? {} : { workspacePath: input.workspacePath }),
175
173
  },
176
174
  derivedHints: {
177
175
  stage: input.sentinel === 'DONE' ? 'done' : input.projection.wake.stage,
@@ -468,7 +466,8 @@ export function createTickRunner(deps) {
468
466
  const owner = await deps.resourceIndex.resolve(resourceUri);
469
467
  if (persisted.workItemKey !== UNRESOLVED_WORK_ITEM_KEY &&
470
468
  owner === undefined &&
471
- (await deps.stateStore.readEventEnvelope(`${persisted.workItemKey}-origin-correlation`)) === null) {
469
+ (await deps.stateStore.readEventEnvelope(`${persisted.workItemKey}-origin-correlation`)) ===
470
+ null) {
472
471
  return [
473
472
  { envelope: persisted, persisted: true },
474
473
  ...buildOriginCorrelationEvents(persisted.workItemKey, unkeyed, resourceUri).map((envelope) => ({ envelope, persisted: false })),
@@ -653,8 +652,7 @@ export function createTickRunner(deps) {
653
652
  async function reconcileStaleRunningRecords(now) {
654
653
  const finishedAt = now.toISOString();
655
654
  const runRecords = await deps.stateStore.listRunRecords();
656
- const staleRecords = runRecords
657
- .filter((record) => isStaleRunningRecord(record, now));
655
+ const staleRecords = runRecords.filter((record) => isStaleRunningRecord(record, now));
658
656
  for (const record of staleRecords) {
659
657
  // Run records carry the work item they belong to, so this is a direct
660
658
  // O(1) read — no scan, no index, no source ambiguity. The record's
@@ -949,8 +947,7 @@ export function createTickRunner(deps) {
949
947
  if (isAwaitingApproval(issue)) {
950
948
  return policy.needsWakeAction(issue, workflow);
951
949
  }
952
- const nextAction = policy.chooseAction(issue, workflow) ??
953
- policy.chooseRetryActionAfterHumanReply(issue);
950
+ const nextAction = policy.chooseAction(issue, workflow) ?? policy.chooseRetryActionAfterHumanReply(issue);
954
951
  return nextAction !== null && policy.needsWakeAction(issue, workflow);
955
952
  });
956
953
  if (candidate === undefined) {
@@ -1042,9 +1039,7 @@ export function createTickRunner(deps) {
1042
1039
  // scratch (#258 follow-up incident: a FAILED `revise` run fell
1043
1040
  // back to a full fresh `implement` run and lost the PR-feedback
1044
1041
  // context).
1045
- const nextAction = policy.chooseRetryActionAfterHumanReply(candidate) ??
1046
- workflowAction?.action ??
1047
- null;
1042
+ const nextAction = policy.chooseRetryActionAfterHumanReply(candidate) ?? workflowAction?.action ?? null;
1048
1043
  if (nextAction === null) {
1049
1044
  return { status: 'idle' };
1050
1045
  }
@@ -1146,9 +1141,7 @@ export function createTickRunner(deps) {
1146
1141
  // An agent that writes DONE but was told not to skip approval has violated
1147
1142
  // the protocol; treat it as AWAITING_APPROVAL so the gate is enforced.
1148
1143
  const skipApproval = runnerResult.metadata?.skipApproval;
1149
- const sentinel = rawSentinel === 'DONE' && skipApproval === false
1150
- ? 'AWAITING_APPROVAL'
1151
- : rawSentinel;
1144
+ const sentinel = rawSentinel === 'DONE' && skipApproval === false ? 'AWAITING_APPROVAL' : rawSentinel;
1152
1145
  const nextStage = lifecycle.nextStageFromSentinel(claimedStage, sentinel, workflow);
1153
1146
  const finishedAt = deps.clock.now().toISOString();
1154
1147
  if (workspaceMode === 'branch') {
@@ -15,7 +15,7 @@ export function maxConfiguredRunnerTimeoutMs(config) {
15
15
  }
16
16
  const registryTimeouts = [...activeRunnerNames]
17
17
  .map((name) => config.runners[name])
18
- .map((entry) => entry === undefined || entry.kind === 'fake' ? undefined : entry.timeoutMs)
18
+ .map((entry) => (entry === undefined || entry.kind === 'fake' ? undefined : entry.timeoutMs))
19
19
  .filter((timeout) => timeout !== undefined);
20
20
  return registryTimeouts.length > 0 ? Math.max(...registryTimeouts) : Infinity;
21
21
  }