@atolis-hq/wake 0.3.56 → 0.3.58

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.
@@ -33,6 +33,8 @@ execution:
33
33
  standard: [fake]
34
34
  deep: [fake]
35
35
  defaultRunnerPool: standard
36
+ # A resumed agent session must have complete durable usage under this limit.
37
+ maxResumableSessionTokens: 200000
36
38
 
37
39
  # Tick dispatch cap and resident-loop idle backoff; the built-in defaults
38
40
  # are fine for a first run.
@@ -108,4 +108,4 @@ export function resolveWakeVersion(options = {}) {
108
108
  return `g${headHash.slice(0, 7)}`;
109
109
  return '0.1.0-dev';
110
110
  }
111
- export const wakeVersion = "ga125b02";
111
+ export const wakeVersion = "g82612c6";
@@ -39,7 +39,7 @@ async function attemptExecution(runtime, activation, context) {
39
39
  stage: activation.stage,
40
40
  ...(context.sessionPolicy === undefined ? {} : { policy: context.sessionPolicy }),
41
41
  };
42
- const resume = resumeContextFor(resumeCandidates, runner, resumeScope);
42
+ const resume = resumeContextFor(resumeCandidates, runner, resumeScope, runtime.config.maxResumableSessionTokens ?? 200_000);
43
43
  const existing = existingRun(prior, runtime.dependencies.clock, owner);
44
44
  if (existing !== undefined)
45
45
  return existing;
@@ -233,19 +233,22 @@ async function releasePreStartResources(runtime, activation, currentRunId, lease
233
233
  export function resumeSessionIdFor(prior, cli, scope, runnerName) {
234
234
  return resumeRunFor(prior, cli, runnerName, scope)?.agent?.metadata.sessionId;
235
235
  }
236
- function resumeContextFor(candidates, runner, scope) {
236
+ function resumeContextFor(candidates, runner, scope, maximumTokens = 200_000) {
237
237
  if (runner.supportsSessionResume !== true)
238
238
  return {};
239
239
  const resumedRun = resumeRunFor(candidates, runner.cli, runner.name, scope);
240
240
  const sessionId = resumedRun?.agent?.metadata.sessionId;
241
+ const usageBaseline = sessionId === undefined
242
+ ? undefined
243
+ : usageBaselineFor(candidates, runner.cli, sessionId, scope, runner.name);
244
+ if (sessionId === undefined ||
245
+ usageBaseline === undefined ||
246
+ usageBaseline.input + usageBaseline.output > maximumTokens)
247
+ return {};
241
248
  return {
242
- ...(sessionId === undefined ? {} : { sessionId }),
249
+ sessionId,
243
250
  ...(resumedRun?.startedAt === undefined ? {} : { startedAt: resumedRun.startedAt }),
244
- ...(sessionId === undefined
245
- ? {}
246
- : {
247
- usageBaseline: usageBaselineFor(candidates, runner.cli, sessionId, scope, runner.name),
248
- }),
251
+ usageBaseline,
249
252
  };
250
253
  }
251
254
  function resumeRunFor(prior, cli, runnerName, scope) {
@@ -31,5 +31,6 @@ export const executionConfigSchema = z
31
31
  leaseDurationMs: z.number().int().positive().optional(),
32
32
  leaseRenewalIntervalMs: z.number().int().positive().optional(),
33
33
  maxAmbiguityReconciliationAttempts: z.number().int().positive().optional(),
34
+ maxResumableSessionTokens: z.number().int().positive().default(200_000),
34
35
  })
35
36
  .strict();
@@ -9,7 +9,7 @@ export function createGitHubAgentContextReader(journal, resources, options = {})
9
9
  const commentHistory = createCommentHistoryReader(journal, resources, options);
10
10
  return {
11
11
  async forWorkItem(workItemId, options) {
12
- const comments = await commentHistory.forWorkItem(workItemId, options);
12
+ const comments = boundedAgentContextComments(await commentHistory.forWorkItem(workItemId, options));
13
13
  return {
14
14
  ...(await currentWorkItemContent(journal, resources, workItemId)),
15
15
  comments,
@@ -18,6 +18,64 @@ export function createGitHubAgentContextReader(journal, resources, options = {})
18
18
  },
19
19
  };
20
20
  }
21
+ const maximumAgentContextComments = 12;
22
+ const maximumAgentContextCommentCharacters = 8_000;
23
+ const maximumAgentContextCharacters = 48_000;
24
+ const truncationNotice = '\n[Wake truncated this historical comment for context bounds.]';
25
+ function boundedAgentContextComments(comments) {
26
+ const indexed = comments.map((comment, index) => ({ comment, index }));
27
+ const latestWakeReviewerFeedback = [...indexed]
28
+ .reverse()
29
+ .find(({ comment }) => isWakeReviewerFeedback(comment));
30
+ const latestWakeAgentArtifact = [...indexed]
31
+ .reverse()
32
+ .find(({ comment }) => isWakeAgentArtifact(comment));
33
+ const protectedWakeArtifacts = [latestWakeReviewerFeedback, latestWakeAgentArtifact].flatMap((candidate, index, values) => candidate === undefined || values.slice(0, index).some((value) => value === candidate)
34
+ ? []
35
+ : [candidate]);
36
+ const retained = [];
37
+ let characters = 0;
38
+ for (const artifact of protectedWakeArtifacts) {
39
+ const remaining = maximumAgentContextCharacters - characters;
40
+ if (remaining <= 0)
41
+ break;
42
+ const body = truncateComment(artifact.comment.body, Math.min(maximumAgentContextCommentCharacters, remaining));
43
+ retained.push({ ...artifact, comment: { ...artifact.comment, body } });
44
+ characters += body.length;
45
+ }
46
+ for (const candidate of [...indexed].reverse()) {
47
+ const { comment } = candidate;
48
+ if ((isWakeDelivery(comment.body) && !protectedWakeArtifacts.includes(candidate)) ||
49
+ protectedWakeArtifacts.includes(candidate) ||
50
+ retained.length === maximumAgentContextComments)
51
+ continue;
52
+ const remaining = maximumAgentContextCharacters - characters;
53
+ if (remaining <= 0)
54
+ break;
55
+ const body = truncateComment(comment.body, Math.min(maximumAgentContextCommentCharacters, remaining));
56
+ retained.push({ ...candidate, comment: { ...comment, body } });
57
+ characters += body.length;
58
+ }
59
+ return retained.sort((left, right) => left.index - right.index).map(({ comment }) => comment);
60
+ }
61
+ function isWakeDelivery(body) {
62
+ return /<!--\s*wake:delivery:[^\s>]+\s*-->/.test(body);
63
+ }
64
+ function isWakeReviewerFeedback(comment) {
65
+ return (comment.body.includes('<!-- wake:agent -->') &&
66
+ (comment.body.includes('**Outcome:** 🔴 Changes Requested') ||
67
+ /"watchGateVerdict"[\s\S]*"outcome"\s*:\s*"REJECTED"/.test(comment.body)));
68
+ }
69
+ function isWakeAgentArtifact(comment) {
70
+ return comment.body.includes('<!-- wake:agent -->') && isWakeDelivery(comment.body);
71
+ }
72
+ function truncateComment(body, maximumCharacters) {
73
+ if (body.length <= maximumCharacters)
74
+ return body;
75
+ if (maximumCharacters <= truncationNotice.length)
76
+ return body.slice(0, maximumCharacters);
77
+ return `${body.slice(0, maximumCharacters - truncationNotice.length)}${truncationNotice}`;
78
+ }
21
79
  async function currentWorkItemContent(journal, resources, workItemId) {
22
80
  const primary = (await resources.correlationsForWork(workItemId)).find((correlation) => correlation.role === ResourceCorrelationRole.Primary);
23
81
  if (primary === undefined)
@@ -39,7 +39,22 @@ export class ProjectionRunner {
39
39
  if (this.caughtUpToGlobalPosition !== undefined &&
40
40
  latestGlobalPosition <= this.caughtUpToGlobalPosition)
41
41
  return 0;
42
- const counts = await Promise.all(this.registered.map((definition) => this.applyFrom(definition, allEvents, limit)));
42
+ let failed = false;
43
+ let firstFailure;
44
+ const counts = await Promise.all(this.registered.map(async (definition) => {
45
+ try {
46
+ return await this.applyFrom(definition, allEvents, limit);
47
+ }
48
+ catch (error) {
49
+ if (!failed) {
50
+ failed = true;
51
+ firstFailure = error;
52
+ }
53
+ return 0;
54
+ }
55
+ }));
56
+ if (failed)
57
+ throw firstFailure;
43
58
  if (counts.every((count) => count < limit))
44
59
  this.caughtUpToGlobalPosition = latestGlobalPosition;
45
60
  return counts.reduce((total, count) => total + count, 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atolis-hq/wake",
3
- "version": "0.3.56",
3
+ "version": "0.3.58",
4
4
  "description": "Local autonomous agent control plane for software development",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {