@atolis-hq/wake 0.2.20 → 0.2.22

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.
@@ -88,6 +88,12 @@ export function createControlPlane(deps) {
88
88
  const status = result?.status;
89
89
  if (status === 'processed') {
90
90
  input.setIdleTicks(0);
91
+ if (input.label === 'intake') {
92
+ await sleepUntilNextTick(deps.intervalMs, {
93
+ getLastRequest: input.getLastRequest,
94
+ setLastRequest: input.setLastRequest,
95
+ });
96
+ }
91
97
  }
92
98
  else {
93
99
  const wait = await sleepUntilNextTick(nextSleepMs(input.getIdleTicks()), {
@@ -5,6 +5,16 @@ function isAwaitingApproval(issue) {
5
5
  const context = issue.context;
6
6
  return context.lastRunSentinel === awaitingApprovalRunnerSentinel;
7
7
  }
8
+ function belowFailureRetryLimit(issue, config) {
9
+ if (config === undefined) {
10
+ return true;
11
+ }
12
+ const context = issue.context;
13
+ const failureCount = typeof context.failureCount === 'number' && Number.isInteger(context.failureCount)
14
+ ? context.failureCount
15
+ : 0;
16
+ return failureCount < config.retry.maxFailureRetries;
17
+ }
8
18
  // Commands are matched as a token at the start of a (trimmed) line, not as a
9
19
  // substring anywhere in the body — so "I have *not* /approved this yet" or a
10
20
  // quoted reply containing /approved does not approve the gate.
@@ -77,7 +87,7 @@ export function createPolicyEngine() {
77
87
  requiredAssignees: config.sources.github.policy.requiredAssignees,
78
88
  });
79
89
  },
80
- needsWakeAction(issue, workflow = builtInDefaultWorkflowDefinition) {
90
+ needsWakeAction(issue, workflow = builtInDefaultWorkflowDefinition, config) {
81
91
  const context = issue.context;
82
92
  const handledCommentId = typeof context.lastHandledCommentId === 'string' ? context.lastHandledCommentId : undefined;
83
93
  const lastCompletedAction = typeof context.lastCompletedAction === 'string' ? context.lastCompletedAction : undefined;
@@ -101,7 +111,7 @@ export function createPolicyEngine() {
101
111
  return false;
102
112
  }
103
113
  if (lastFailureClass === 'quota') {
104
- return true;
114
+ return belowFailureRetryLimit(issue, config);
105
115
  }
106
116
  const workflowAction = chooseWorkflowAction(issue, workflow);
107
117
  return workflowAction !== null && lastCompletedAction !== workflowAction.action;
@@ -219,7 +229,7 @@ export function createPolicyEngine() {
219
229
  const nextAction = resolveCustomCommand(issue, config)?.action ??
220
230
  this.chooseAction(issue, workflow) ??
221
231
  this.chooseRetryActionAfterHumanReply(issue, workflow);
222
- if (nextAction === null || !this.needsWakeAction(issue, workflow)) {
232
+ if (nextAction === null || !this.needsWakeAction(issue, workflow, config)) {
223
233
  return null;
224
234
  }
225
235
  return { action: nextAction, workflow };
@@ -203,9 +203,18 @@ async function applyEvent(current, event, ctx, config) {
203
203
  config !== undefined &&
204
204
  isCustomCommandAction(payload.action, config);
205
205
  const shouldClearSession = isForwardProgression || isFailed;
206
+ const currentFailureCount = typeof current.context.failureCount === 'number' &&
207
+ Number.isInteger(current.context.failureCount)
208
+ ? current.context.failureCount
209
+ : 0;
206
210
  const nextContext = {
207
211
  ...current.context,
208
212
  lastFailureClass: payload.failureClass,
213
+ failureCount: payload.failureClass !== undefined
214
+ ? currentFailureCount + 1
215
+ : payload.sentinel === doneRunnerSentinel || payload.sentinel === 'AWAITING_APPROVAL'
216
+ ? 0
217
+ : currentFailureCount,
209
218
  ...(payload.handledCommentId === undefined
210
219
  ? {}
211
220
  : { lastHandledCommentId: payload.handledCommentId }),
@@ -457,6 +457,11 @@ const wakeConfigBaseSchema = z.object({
457
457
  retainAfterWorkspaceCleanup: z.boolean().default(false),
458
458
  })
459
459
  .default({ enabled: false, retainAfterWorkspaceCleanup: false }),
460
+ retry: z
461
+ .object({
462
+ maxFailureRetries: z.number().int().positive().default(5),
463
+ })
464
+ .default({ maxFailureRetries: 5 }),
460
465
  runners: z.record(z.string(), runnerEntrySchema).default({
461
466
  fake: { kind: 'fake', cli: 'Fake' },
462
467
  'claude-haiku': {
@@ -656,6 +661,7 @@ export const wakeInfraConfigSchema = wakeConfigBaseSchema.pick({
656
661
  dev: true,
657
662
  scheduler: true,
658
663
  transcripts: true,
664
+ retry: true,
659
665
  ui: true,
660
666
  sources: true,
661
667
  sinks: true,
@@ -124,4 +124,4 @@ export function resolveWakeVersion(options = {}) {
124
124
  }
125
125
  return '0.1.0-dev';
126
126
  }
127
- export const wakeVersion = "ge9439a7";
127
+ export const wakeVersion = "g1dfa9f8";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atolis-hq/wake",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
4
4
  "description": "Local autonomous agent control plane for software development",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {