@atolis-hq/wake 0.1.5 → 0.1.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 (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-issues-work-source.js +8 -14
  11. package/dist/src/adapters/github/github-pull-request-activity-source.js +44 -9
  12. package/dist/src/adapters/http/ui-data.js +23 -12
  13. package/dist/src/adapters/http/ui-server.js +14 -4
  14. package/dist/src/adapters/runner/runner-registry.js +2 -4
  15. package/dist/src/adapters/runner/stage-prompt.js +15 -18
  16. package/dist/src/cli/sandbox-command.js +8 -6
  17. package/dist/src/cli/startup-preflight.js +1 -1
  18. package/dist/src/core/control-plane.js +6 -2
  19. package/dist/src/core/policy-engine.js +20 -21
  20. package/dist/src/core/projection-updater.js +11 -14
  21. package/dist/src/core/quota-backoff.js +1 -1
  22. package/dist/src/core/sink-router.js +5 -7
  23. package/dist/src/core/tick-runner.js +88 -73
  24. package/dist/src/domain/runner-routing.js +1 -1
  25. package/dist/src/domain/schema.js +213 -58
  26. package/dist/src/domain/stages.js +1 -4
  27. package/dist/src/domain/workflows.js +1 -3
  28. package/dist/src/lib/lock.js +1 -1
  29. package/dist/src/main.js +5 -8
  30. package/dist/src/version.js +1 -1
  31. package/package.json +10 -2
  32. package/prompts/codereview.md +40 -0
  33. package/prompts/implement.md +4 -0
  34. package/prompts/refine.md +2 -0
  35. package/prompts/revise.md +4 -3
package/README.md CHANGED
@@ -63,7 +63,7 @@ For more detail, see [docs/vision.md](docs/vision.md) and
63
63
  - **Human resumption.** A human can pick up the exact local agent session when a
64
64
  direct terminal intervention is the best way forward.
65
65
  - **Operator correlation escape hatch.** `wake correlate <workItemKey>
66
- <resourceUri>` lets an operator hand-declare that a resource (a PR, a Slack
66
+ <resourceUri>` lets an operator hand-declare that a resource (a PR, a Slack
67
67
  thread, etc.) belongs to an existing work item when nothing detected the
68
68
  link automatically. See [docs/configuration.md](docs/configuration.md).
69
69
 
@@ -12,12 +12,7 @@ function slugify(value, maxLength = 40) {
12
12
  .replace(/-+$/g, '');
13
13
  }
14
14
  export function buildWakeSessionName(input) {
15
- return [
16
- input.sessionName,
17
- `issue-${input.issueNumber}`,
18
- slugify(input.title),
19
- input.runId,
20
- ]
15
+ return [input.sessionName, `issue-${input.issueNumber}`, slugify(input.title), input.runId]
21
16
  .filter((part) => part.length > 0)
22
17
  .join('-');
23
18
  }
@@ -54,13 +49,9 @@ export function buildClaudePrintArgs(options) {
54
49
  options.sessionName,
55
50
  ...(options.resumeSessionId === undefined ? [] : ['--resume', options.resumeSessionId]),
56
51
  ...(options.effort === undefined ? [] : ['--effort', options.effort]),
57
- ...(options.systemPrompt === undefined
58
- ? []
59
- : ['--append-system-prompt', options.systemPrompt]),
52
+ ...(options.systemPrompt === undefined ? [] : ['--append-system-prompt', options.systemPrompt]),
60
53
  ...(options.maxTurns === undefined ? [] : ['--max-turns', String(options.maxTurns)]),
61
- ...(options.permissionMode === undefined
62
- ? []
63
- : ['--permission-mode', options.permissionMode]),
54
+ ...(options.permissionMode === undefined ? [] : ['--permission-mode', options.permissionMode]),
64
55
  ...(options.allowedTools === undefined || options.allowedTools.length === 0
65
56
  ? []
66
57
  : ['--allowedTools', options.allowedTools.join(' ')]),
@@ -199,9 +190,7 @@ export function createClaudeRunner(options) {
199
190
  ...(stagePrompt.permissionMode === undefined
200
191
  ? {}
201
192
  : { permissionMode: stagePrompt.permissionMode }),
202
- ...(options.settings.remoteControl.enabled
203
- ? { remoteControlName: sessionName }
204
- : {}),
193
+ ...(options.settings.remoteControl.enabled ? { remoteControlName: sessionName } : {}),
205
194
  ...(options.settings.effort === undefined ? {} : { effort: options.settings.effort }),
206
195
  ...(isResume ? { resumeSessionId: priorSessionId } : {}),
207
196
  });
@@ -222,9 +211,7 @@ export function createClaudeRunner(options) {
222
211
  repo: input.projection.issue.repo,
223
212
  recentEventIds: input.recentEvents.map((event) => event.eventId),
224
213
  model,
225
- ...(input.workspacePath === undefined
226
- ? {}
227
- : { workspacePath: input.workspacePath }),
214
+ ...(input.workspacePath === undefined ? {} : { workspacePath: input.workspacePath }),
228
215
  }));
229
216
  const result = await runClaudeCommand({
230
217
  command: options.command,
@@ -256,9 +243,7 @@ export function createClaudeRunner(options) {
256
243
  repo: input.projection.issue.repo,
257
244
  recentEventIds: input.recentEvents.map((event) => event.eventId),
258
245
  model,
259
- ...(input.workspacePath === undefined
260
- ? {}
261
- : { workspacePath: input.workspacePath }),
246
+ ...(input.workspacePath === undefined ? {} : { workspacePath: input.workspacePath }),
262
247
  exitCode: result.exitCode,
263
248
  }));
264
249
  let parsedReason;
@@ -314,9 +299,7 @@ export function createClaudeRunner(options) {
314
299
  repo: input.projection.issue.repo,
315
300
  recentEventIds: input.recentEvents.map((event) => event.eventId),
316
301
  model,
317
- ...(input.workspacePath === undefined
318
- ? {}
319
- : { workspacePath: input.workspacePath }),
302
+ ...(input.workspacePath === undefined ? {} : { workspacePath: input.workspacePath }),
320
303
  ...(parsed.session_id === undefined ? {} : { sessionId: parsed.session_id }),
321
304
  }));
322
305
  const tokenUsage = extractTokenUsage(parsed);
@@ -327,9 +310,7 @@ export function createClaudeRunner(options) {
327
310
  ...(parseRunnerResult(parsed.result).status === 'FAILED'
328
311
  ? { failureClass: 'task' }
329
312
  : {}),
330
- ...(parsed.session_id === undefined
331
- ? {}
332
- : { session_id: parsed.session_id }),
313
+ ...(parsed.session_id === undefined ? {} : { session_id: parsed.session_id }),
333
314
  ...(tokenUsage === undefined ? {} : { tokenUsage }),
334
315
  metadata: {
335
316
  stdout: result.stdout,
@@ -358,9 +339,7 @@ export function createClaudeRunner(options) {
358
339
  : undefined;
359
340
  return {
360
341
  text: parsed?.result ?? '',
361
- ...(parsed?.session_id === undefined
362
- ? {}
363
- : { sessionId: parsed.session_id }),
342
+ ...(parsed?.session_id === undefined ? {} : { sessionId: parsed.session_id }),
364
343
  stdout: result.stdout,
365
344
  stderr: result.stderr,
366
345
  exitCode: result.exitCode,
@@ -246,9 +246,7 @@ export function createCodexRunner(options) {
246
246
  repo: input.projection.issue.repo,
247
247
  recentEventIds: input.recentEvents.map((event) => event.eventId),
248
248
  model,
249
- ...(input.workspacePath === undefined
250
- ? {}
251
- : { workspacePath: input.workspacePath }),
249
+ ...(input.workspacePath === undefined ? {} : { workspacePath: input.workspacePath }),
252
250
  }));
253
251
  const result = await runAgentCliCommand({
254
252
  command: options.command,
@@ -291,9 +289,7 @@ export function createCodexRunner(options) {
291
289
  repo: input.projection.issue.repo,
292
290
  recentEventIds: input.recentEvents.map((event) => event.eventId),
293
291
  model,
294
- ...(input.workspacePath === undefined
295
- ? {}
296
- : { workspacePath: input.workspacePath }),
292
+ ...(input.workspacePath === undefined ? {} : { workspacePath: input.workspacePath }),
297
293
  exitCode: result.exitCode,
298
294
  }));
299
295
  return {
@@ -302,7 +298,8 @@ export function createCodexRunner(options) {
302
298
  ? `Codex runner timed out after ${options.settings.timeoutMs}ms and was killed`
303
299
  : structuredMessage !== undefined
304
300
  ? `Codex runner failed: ${structuredMessage}`
305
- : result.stdout.trim().length === 0 ? 'Codex runner produced no output'
301
+ : result.stdout.trim().length === 0
302
+ ? 'Codex runner produced no output'
306
303
  : 'Codex runner failed',
307
304
  result.stderr,
308
305
  sandboxLog?.text,
@@ -334,9 +331,7 @@ export function createCodexRunner(options) {
334
331
  repo: input.projection.issue.repo,
335
332
  recentEventIds: input.recentEvents.map((event) => event.eventId),
336
333
  model,
337
- ...(input.workspacePath === undefined
338
- ? {}
339
- : { workspacePath: input.workspacePath }),
334
+ ...(input.workspacePath === undefined ? {} : { workspacePath: input.workspacePath }),
340
335
  ...(parsed.sessionId === undefined ? {} : { sessionId: parsed.sessionId }),
341
336
  }));
342
337
  return {
@@ -65,7 +65,11 @@ export function createFakeGitHubPullRequestActivitySource(options) {
65
65
  occurredAt: publishedAt,
66
66
  ingestedAt: publishedAt,
67
67
  trigger: 'context-only',
68
- payload: { intentEventId: input.event.eventId, kind: input.event.payload.kind, body: input.event.payload.body },
68
+ payload: {
69
+ intentEventId: input.event.eventId,
70
+ kind: input.event.payload.kind,
71
+ body: input.event.payload.body,
72
+ },
69
73
  }),
70
74
  ];
71
75
  },
@@ -1,7 +1,7 @@
1
1
  export function createFakeRunner(result, options) {
2
2
  return {
3
3
  async run(_) {
4
- return result ?? {
4
+ return (result ?? {
5
5
  result: [
6
6
  'Fake runner completed',
7
7
  '',
@@ -16,7 +16,7 @@ export function createFakeRunner(result, options) {
16
16
  metadata: {
17
17
  source: 'fake-runner',
18
18
  },
19
- };
19
+ });
20
20
  },
21
21
  };
22
22
  }
@@ -92,8 +92,7 @@ export function createFakeTicketingSystem(options) {
92
92
  ? input.event.payload.stageLabel
93
93
  : undefined;
94
94
  const labels = [
95
- ...currentLabels.filter((label) => !label.startsWith('wake:status.') &&
96
- !label.startsWith('wake:stage.')),
95
+ ...currentLabels.filter((label) => !label.startsWith('wake:status.') && !label.startsWith('wake:stage.')),
97
96
  ...(statusLabel === undefined
98
97
  ? currentLabels.filter((label) => label.startsWith('wake:status.'))
99
98
  : [statusLabel]),
@@ -155,12 +154,8 @@ export async function createFileBackedFakeTicketingSystem(options) {
155
154
  await access(options.fixturePath);
156
155
  }
157
156
  catch {
158
- return createFakeTicketingSystem(options.now === undefined
159
- ? { tickets: [] }
160
- : { tickets: [], now: options.now });
157
+ return createFakeTicketingSystem(options.now === undefined ? { tickets: [] } : { tickets: [], now: options.now });
161
158
  }
162
159
  const raw = await readJsonFile(options.fixturePath);
163
- return createFakeTicketingSystem(options.now === undefined
164
- ? { tickets: raw }
165
- : { tickets: raw, now: options.now });
160
+ return createFakeTicketingSystem(options.now === undefined ? { tickets: raw } : { tickets: raw, now: options.now });
166
161
  }
@@ -2,7 +2,7 @@ import { mkdir, rm } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  export function createFakeWorkspaceManager(root) {
4
4
  return {
5
- async prepareWorkspace({ workId, }) {
5
+ async prepareWorkspace({ workId }) {
6
6
  // Keyed on the work id, symmetrically with the real git-backed manager.
7
7
  const workspacePath = join(root, workId);
8
8
  await mkdir(workspacePath, { recursive: true });
@@ -45,10 +45,10 @@ async function readEventFile(file) {
45
45
  }
46
46
  function issueArchiveAgeDate(item) {
47
47
  const stageChangedAt = item.wake.stageHistory.at(-1)?.changedAt;
48
- return [stageChangedAt, item.wake.syncedAt, item.issue.updatedAt]
48
+ return ([stageChangedAt, item.wake.syncedAt, item.issue.updatedAt]
49
49
  .filter((value) => value !== undefined)
50
50
  .sort()
51
- .at(-1) ?? item.wake.syncedAt;
51
+ .at(-1) ?? item.wake.syncedAt);
52
52
  }
53
53
  function shouldArchiveIssueState(item, options) {
54
54
  if (!isTerminalStage(item.wake.stage) && item.issue.state !== 'closed') {
@@ -61,9 +61,7 @@ export async function listRunRecords(wakeRoot) {
61
61
  const runsRoot = join(wakeRoot, 'runs');
62
62
  const recordsById = new Map();
63
63
  try {
64
- const files = (await readdir(runsRoot))
65
- .filter((file) => file.endsWith('.json'))
66
- .sort();
64
+ const files = (await readdir(runsRoot)).filter((file) => file.endsWith('.json')).sort();
67
65
  for (const file of files) {
68
66
  const record = await readRunRecordFile(join(runsRoot, file));
69
67
  if (record !== null) {
@@ -122,9 +120,7 @@ async function listRunRecordsForDate(wakeRoot, date) {
122
120
  async function listRecentRunRecords(wakeRoot, limit) {
123
121
  const runsRoot = join(wakeRoot, 'runs');
124
122
  const recordsById = new Map();
125
- const dateDirs = (await readdir(join(runsRoot, 'by-date')).catch(() => []))
126
- .sort()
127
- .reverse();
123
+ const dateDirs = (await readdir(join(runsRoot, 'by-date')).catch(() => [])).sort().reverse();
128
124
  for (const dateDir of dateDirs) {
129
125
  const records = await listRunRecordsForDate(wakeRoot, dateDir);
130
126
  for (const record of records.reverse()) {
@@ -265,7 +261,9 @@ export function createStateStore({ wakeRoot }) {
265
261
  if (record === null) {
266
262
  continue;
267
263
  }
268
- if (archiveOptions !== null && !isArchive && shouldArchiveIssueState(record, archiveOptions)) {
264
+ if (archiveOptions !== null &&
265
+ !isArchive &&
266
+ shouldArchiveIssueState(record, archiveOptions)) {
269
267
  const archivePath = paths.archivedWorkItemStateFile(record.workItemKey);
270
268
  await mkdir(dirname(archivePath), { recursive: true });
271
269
  await rename(file, archivePath).catch(() => undefined);
@@ -291,7 +289,7 @@ export function createStateStore({ wakeRoot }) {
291
289
  const files = (await readdir(eventsRoot)).sort();
292
290
  const envelopes = [];
293
291
  for (const file of files) {
294
- envelopes.push(...await readEventFile(join(eventsRoot, file)));
292
+ envelopes.push(...(await readEventFile(join(eventsRoot, file))));
295
293
  }
296
294
  return envelopes;
297
295
  }
@@ -11,6 +11,6 @@ export async function resolveGitHubToken(deps) {
11
11
  return token;
12
12
  }
13
13
  catch (error) {
14
- throw new Error(`Failed to resolve GitHub token via gh auth token: ${error instanceof Error ? error.message : String(error)}`);
14
+ throw new Error(`Failed to resolve GitHub token via gh auth token: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
15
15
  }
16
16
  }
@@ -60,14 +60,11 @@ function normalizeTicketUpsert(input) {
60
60
  issueUpdatedAt: input.issue.updated_at,
61
61
  },
62
62
  },
63
- ...(input.expectedEcho === true
64
- ? { derivedHints: { expectedEcho: true } }
65
- : {}),
63
+ ...(input.expectedEcho === true ? { derivedHints: { expectedEcho: true } } : {}),
66
64
  });
67
65
  }
68
66
  function normalizeTicketCommentEvent(input) {
69
- const isUpdate = input.existingUpdatedAt !== undefined &&
70
- input.existingUpdatedAt !== input.comment.updated_at;
67
+ const isUpdate = input.existingUpdatedAt !== undefined && input.existingUpdatedAt !== input.comment.updated_at;
71
68
  return createUnkeyedEventEnvelope({
72
69
  eventId: `github-comment-${input.repo}-${input.issueNumber}-${input.comment.id}-${input.comment.updated_at}`,
73
70
  streamScope: 'work-item',
@@ -96,9 +93,7 @@ function normalizeTicketCommentEvent(input) {
96
93
  createdAt: input.comment.created_at,
97
94
  updatedAt: input.comment.updated_at,
98
95
  },
99
- providerEventType: isUpdate
100
- ? 'github.issue.comment.updated'
101
- : 'github.issue.comment.created',
96
+ providerEventType: isUpdate ? 'github.issue.comment.updated' : 'github.issue.comment.created',
102
97
  },
103
98
  derivedHints: {
104
99
  // Third-party bots/integrations (CI, Dependabot, Renovate, etc.) must
@@ -209,7 +204,9 @@ export function formatWakeComment(payload, controlPlaneUrl) {
209
204
  cost === undefined ? undefined : `cost ${cost}`,
210
205
  runId === undefined ? undefined : `run \`${runId}\``,
211
206
  ].filter((part) => part !== undefined);
212
- const name = controlPlaneUrl === undefined ? defaultAgentIdentity : `[${defaultAgentIdentity}](${controlPlaneUrl})`;
207
+ const name = controlPlaneUrl === undefined
208
+ ? defaultAgentIdentity
209
+ : `[${defaultAgentIdentity}](${controlPlaneUrl})`;
213
210
  const header = `**${name}** _(Wake ${wakeVersion}${details.length > 0 ? ` · ${details.join(' · ')}` : ''})_`;
214
211
  const sections = [wakeCommentMarker, header, body];
215
212
  if (kind === 'approval-request') {
@@ -304,9 +301,7 @@ export function createGitHubIssuesWorkSource(deps) {
304
301
  comment,
305
302
  ingestedAt,
306
303
  ...(deps.selfLogin === undefined ? {} : { selfLogin: deps.selfLogin }),
307
- ...(known?.updatedAt === undefined
308
- ? {}
309
- : { existingUpdatedAt: known.updatedAt }),
304
+ ...(known?.updatedAt === undefined ? {} : { existingUpdatedAt: known.updatedAt }),
310
305
  }));
311
306
  }
312
307
  }
@@ -346,8 +341,7 @@ export function createGitHubIssuesWorkSource(deps) {
346
341
  ? input.event.payload.stageLabel
347
342
  : undefined;
348
343
  const nextLabels = [
349
- ...currentLabels.filter((label) => !label.startsWith(wakeStatusLabelPrefix) &&
350
- !label.startsWith(wakeStageLabelPrefix)),
344
+ ...currentLabels.filter((label) => !label.startsWith(wakeStatusLabelPrefix) && !label.startsWith(wakeStageLabelPrefix)),
351
345
  ...(nextStatusLabel !== undefined
352
346
  ? [nextStatusLabel]
353
347
  : currentLabels.filter((label) => label.startsWith(wakeStatusLabelPrefix))),
@@ -97,7 +97,11 @@ export function createGitHubPullRequestActivitySource(deps) {
97
97
  ingestedAt,
98
98
  trigger: 'context-only',
99
99
  payload: {
100
- pr: { number: pr.number, author: pr.user?.login ?? 'unknown', headRef: pr.head.ref },
100
+ pr: {
101
+ number: pr.number,
102
+ author: pr.user?.login ?? 'unknown',
103
+ headRef: pr.head.ref,
104
+ },
101
105
  },
102
106
  }));
103
107
  }
@@ -127,7 +131,12 @@ export function createGitHubPullRequestActivitySource(deps) {
127
131
  direction: 'inbound',
128
132
  sourceSystem: githubPrSource,
129
133
  sourceEventType: 'pr.comment.created',
130
- sourceRefs: { repo: ref.repoRef, commentId: String(comment.id), sourceUrl: comment.html_url, resourceUri },
134
+ sourceRefs: {
135
+ repo: ref.repoRef,
136
+ commentId: String(comment.id),
137
+ sourceUrl: comment.html_url,
138
+ resourceUri,
139
+ },
131
140
  occurredAt: comment.updated_at,
132
141
  ingestedAt,
133
142
  trigger: 'context-only',
@@ -158,7 +167,12 @@ export function createGitHubPullRequestActivitySource(deps) {
158
167
  direction: 'inbound',
159
168
  sourceSystem: githubPrSource,
160
169
  sourceEventType: 'pr.review.created',
161
- sourceRefs: { repo: ref.repoRef, commentId: `review-${review.id}`, sourceUrl: review.html_url, resourceUri },
170
+ sourceRefs: {
171
+ repo: ref.repoRef,
172
+ commentId: `review-${review.id}`,
173
+ sourceUrl: review.html_url,
174
+ resourceUri,
175
+ },
162
176
  occurredAt: submittedAt,
163
177
  ingestedAt,
164
178
  trigger: 'context-only',
@@ -207,7 +221,10 @@ export function createGitHubPullRequestActivitySource(deps) {
207
221
  createdAt: comment.created_at,
208
222
  updatedAt: comment.updated_at,
209
223
  resourceUri: threadUri,
210
- reviewThread: { path: comment.path, line: comment.line ?? comment.original_line ?? undefined },
224
+ reviewThread: {
225
+ path: comment.path,
226
+ line: comment.line ?? comment.original_line ?? undefined,
227
+ },
211
228
  },
212
229
  },
213
230
  derivedHints: { botAuthoredComment: isBotAuthored(comment, deps.selfLogin) },
@@ -287,7 +304,11 @@ export function createGitHubPullRequestActivitySource(deps) {
287
304
  direction: 'inbound',
288
305
  sourceSystem: githubPrSource,
289
306
  sourceEventType: 'pr.checks.failed',
290
- sourceRefs: { repo: ref.repoRef, sourceUrl: status.target_url ?? pr.html_url, resourceUri },
307
+ sourceRefs: {
308
+ repo: ref.repoRef,
309
+ sourceUrl: status.target_url ?? pr.html_url,
310
+ resourceUri,
311
+ },
291
312
  occurredAt: status.updated_at,
292
313
  ingestedAt,
293
314
  trigger: 'context-only',
@@ -345,7 +366,10 @@ export function createGitHubPullRequestActivitySource(deps) {
345
366
  throw new Error(`cannot deliver intent ${input.event.eventId}: malformed review-thread uri ${resourceUri}`);
346
367
  }
347
368
  const [, owner, repo, numberStr, rootIdStr] = match;
348
- if (owner === undefined || repo === undefined || numberStr === undefined || rootIdStr === undefined) {
369
+ if (owner === undefined ||
370
+ repo === undefined ||
371
+ numberStr === undefined ||
372
+ rootIdStr === undefined) {
349
373
  throw new Error(`cannot deliver intent ${input.event.eventId}: malformed review-thread uri ${resourceUri}`);
350
374
  }
351
375
  const response = await deps.client.replyToReviewComment(owner, repo, Number(numberStr), Number(rootIdStr), formatWakeComment(input.event.payload, await readControlPlaneUiUrl(deps.config.paths.wakeRoot)));
@@ -357,11 +381,18 @@ export function createGitHubPullRequestActivitySource(deps) {
357
381
  direction: 'outbound',
358
382
  sourceSystem: githubPrSource,
359
383
  sourceEventType: 'pr.review-comment.reply.published',
360
- sourceRefs: { resourceUri, sourceUrl: response?.html_url },
384
+ sourceRefs: {
385
+ resourceUri,
386
+ sourceUrl: response?.html_url,
387
+ },
361
388
  occurredAt: publishedAt,
362
389
  ingestedAt: publishedAt,
363
390
  trigger: 'context-only',
364
- payload: { intentEventId: input.event.eventId, kind: input.event.payload.kind, body: input.event.payload.body },
391
+ payload: {
392
+ intentEventId: input.event.eventId,
393
+ kind: input.event.payload.kind,
394
+ body: input.event.payload.body,
395
+ },
365
396
  }),
366
397
  ];
367
398
  }
@@ -382,7 +413,11 @@ export function createGitHubPullRequestActivitySource(deps) {
382
413
  occurredAt: publishedAt,
383
414
  ingestedAt: publishedAt,
384
415
  trigger: 'context-only',
385
- payload: { intentEventId: input.event.eventId, kind: input.event.payload.kind, body: input.event.payload.body },
416
+ payload: {
417
+ intentEventId: input.event.eventId,
418
+ kind: input.event.payload.kind,
419
+ body: input.event.payload.body,
420
+ },
386
421
  }),
387
422
  ];
388
423
  },
@@ -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
  };