@atolis-hq/wake 0.1.8 → 0.1.10

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.
@@ -210,7 +210,7 @@ export function formatWakeComment(payload, controlPlaneUrl) {
210
210
  const header = `**${name}** _(Wake ${wakeVersion}${details.length > 0 ? ` · ${details.join(' · ')}` : ''})_`;
211
211
  const sections = [wakeCommentMarker, header, body];
212
212
  if (kind === 'approval-request') {
213
- sections.push('_To approve this work, reply with `/approved`. To request changes, reply with `/changes` followed by your feedback. To ask a question without requesting changes, reply with `/question` followed by your question._');
213
+ sections.push('_To approve this work, reply with `/approved`. To request changes, reply with `/changes` followed by your feedback. To ask a question without requesting changes, reply with `/ask` followed by your question._');
214
214
  }
215
215
  if (sessionId !== undefined) {
216
216
  const resumeCommandArgs = cli === undefined
@@ -2,7 +2,6 @@ import { defaultAgentIdentity } from '../../domain/schema.js';
2
2
  import { chooseAction, workflowForProjection } from '../../domain/workflows.js';
3
3
  import { branchNameForIssue } from '../git/git-workspace-manager.js';
4
4
  import { loadPromptTemplate, renderPromptTemplate } from './prompt-templates.js';
5
- const questionCommandPattern = /^\/question\b/i;
6
5
  function reviewCommentApiId(comment) {
7
6
  // github-pull-request-activity-source.ts composites review-comment ids as
8
7
  // `pr-review-comment-<id>`; strip that prefix back off to recover the raw
@@ -51,22 +50,6 @@ function previousCommentsThroughLastRun(projection) {
51
50
  const cursorIndex = projection.comments.findIndex((comment) => comment.id === handledCommentId);
52
51
  return cursorIndex === -1 ? [] : projection.comments.slice(0, cursorIndex + 1);
53
52
  }
54
- function matchesCommand(body, pattern) {
55
- return body.split(/\r?\n/).some((line) => pattern.test(line.trim()));
56
- }
57
- function latestQuestionCommandNote(input) {
58
- const { comments, successSentinel } = input;
59
- const latestHumanComment = comments.at(-1);
60
- if (latestHumanComment === undefined ||
61
- !matchesCommand(latestHumanComment.body, questionCommandPattern)) {
62
- return '';
63
- }
64
- return [
65
- 'The latest actionable command is `/question`.',
66
- `Answer the question in the resumed session context. Do not make code changes solely because of this command; if the answer does not require changes, leave the work in its current state and report ${successSentinel}.`,
67
- '',
68
- ].join('\n');
69
- }
70
53
  function parseFrontmatterList(value) {
71
54
  if (value === undefined || value.trim().length === 0) {
72
55
  return [];
@@ -203,13 +186,7 @@ export async function buildStagePrompt(input) {
203
186
  const permissionMode = template.frontmatter.permissionMode;
204
187
  const commentsToAddress = newCommentsSinceLastRun(input.projection);
205
188
  const priorComments = previousCommentsThroughLastRun(input.projection);
206
- context.feedbackCommandNote =
207
- mode === 'resume'
208
- ? latestQuestionCommandNote({
209
- comments: commentsToAddress,
210
- successSentinel: skipApproval ? 'DONE' : 'AWAITING_APPROVAL',
211
- })
212
- : '';
189
+ context.feedbackCommandNote = '';
213
190
  const commentSections = mode === 'resume'
214
191
  ? [
215
192
  {
@@ -10,7 +10,6 @@ function isAwaitingApproval(issue) {
10
10
  // quoted reply containing /approved does not approve the gate.
11
11
  const approvedCommandPattern = /^\/approved\b/i;
12
12
  const changesCommandPattern = /^\/changes\b/i;
13
- const questionCommandPattern = /^\/question\b/i;
14
13
  // The action Wake runs when a correlated PR gets new reviewer feedback while
15
14
  // the work item is awaiting approval. Not configurable per workflow: it's a
16
15
  // lateral response to a PR surface, not a workflow stage.
@@ -140,13 +139,12 @@ export function createPolicyEngine() {
140
139
  }
141
140
  const approved = matchesCommand(latestHumanComment.body, approvedCommandPattern);
142
141
  const changesRequested = matchesCommand(latestHumanComment.body, changesCommandPattern);
143
- const questionAsked = matchesCommand(latestHumanComment.body, questionCommandPattern);
144
- // Neither an explicit /approved, /changes, nor /question: treat this as
142
+ // Neither an explicit /approved nor /changes: treat this as
145
143
  // conversation, not a decision. Stay idle rather than re-running the
146
144
  // pending action off the back of an unmarked clarifying question (S2).
147
145
  // The comment stays unhandled, so it's reconsidered on the next tick and
148
146
  // by a human who follows up with an explicit command.
149
- if (!approved && !changesRequested && !questionAsked) {
147
+ if (!approved && !changesRequested) {
150
148
  return null;
151
149
  }
152
150
  return { approved, pendingAction };
@@ -154,7 +152,7 @@ export function createPolicyEngine() {
154
152
  // Callers must try resolveApprovalTransition first and only fall back to
155
153
  // this when it returns null. resolveApprovalTransition doesn't check
156
154
  // resourceUri, so a PR-surface comment that happens to carry an explicit
157
- // /approved, /changes, or /question command is deliberately still routed
155
+ // /approved or /changes command is deliberately still routed
158
156
  // there — this function only ever sees comments resolveApprovalTransition
159
157
  // already passed on (plain PR feedback with no command).
160
158
  resolvePendingReviewFeedback(issue) {
@@ -127,7 +127,7 @@ export function createTickRunner(deps) {
127
127
  // was already replied to.
128
128
  //
129
129
  // Never threads a pr-review-thread surface specifically: this is
130
- // Wake's own status/approval-request/question card, a milestone
130
+ // Wake's own status, approval-request, or question card: a milestone
131
131
  // message, not a targeted reply to one inline comment — burying it
132
132
  // as a reply deep in a single review thread makes it easy to miss.
133
133
  // Omitting resourceUri here falls back to sourceOrigin in
@@ -1,4 +1,4 @@
1
- export const reservedCommandNames = ['approved', 'changes', 'question'];
1
+ export const reservedCommandNames = ['approved', 'changes'];
2
2
  function latestUnhandledHumanComment(issue) {
3
3
  const context = issue.context;
4
4
  const handledCommentId = typeof context.lastHandledCommentId === 'string' ? context.lastHandledCommentId : undefined;
@@ -516,6 +516,11 @@ export const wakeConfigSchema = z
516
516
  },
517
517
  }),
518
518
  commands: z.record(identifierSchema, customCommandSchema).default({
519
+ ask: {
520
+ action: 'ask',
521
+ workspace: 'read-only',
522
+ tier: 'light',
523
+ },
519
524
  codereview: {
520
525
  action: 'codereview',
521
526
  workspace: 'read-only',
package/dist/src/main.js CHANGED
@@ -52,6 +52,18 @@ function routesOnlyToFake(config) {
52
52
  return first !== undefined && config.runners[first]?.kind === 'fake';
53
53
  });
54
54
  }
55
+ async function resolveSelfLogin(githubClient) {
56
+ if (githubClient === undefined) {
57
+ return undefined;
58
+ }
59
+ try {
60
+ return await githubClient.getAuthenticatedLogin();
61
+ }
62
+ catch (error) {
63
+ console.error(`wake: failed to resolve authenticated GitHub login; continuing without self-login bot detection: ${String(error)}`);
64
+ return undefined;
65
+ }
66
+ }
55
67
  export function formatTickFailureDetails(runRecord) {
56
68
  if (runRecord === null) {
57
69
  return null;
@@ -180,7 +192,7 @@ export async function buildRuntime(args) {
180
192
  // by direct API/CLI call (not through formatWakeComment, so it never
181
193
  // carries the wake:agent marker) as bot-authored instead of a fresh human
182
194
  // reply that would re-trigger another run against itself.
183
- const selfLogin = githubClient !== undefined ? await githubClient.getAuthenticatedLogin() : undefined;
195
+ const selfLogin = await resolveSelfLogin(githubClient);
184
196
  const artifactVerifier = prTrackingEnabled && githubClient !== undefined
185
197
  ? createGitHubArtifactVerifier({ client: githubClient })
186
198
  : undefined;
@@ -1 +1 @@
1
- export const wakeVersion = "0.1.8+g8f32cc9";
1
+ export const wakeVersion = "0.1.10+g7ed704e";
@@ -44,6 +44,25 @@ req.setTimeout(1000, () => req.destroy());
44
44
  echo "wake ui: ngrok tunnel started but public URL was not discovered; see /wake/logs/ngrok.log"
45
45
  }
46
46
 
47
+ supervise_wake_start() {
48
+ local restart_delay="${WAKE_START_RESTART_DELAY_SECONDS:-10}"
49
+
50
+ while true; do
51
+ echo "wake start: starting resident loop"
52
+ node /app/dist/src/main.js start \
53
+ --wake-root /wake \
54
+ >> /wake/logs/start.log 2>&1 &
55
+
56
+ local child_pid="$!"
57
+ echo "${child_pid}" > /wake/logs/start.pid
58
+
59
+ wait "${child_pid}"
60
+ local exit_code="$?"
61
+ echo "wake start: resident loop exited with status ${exit_code}; restarting in ${restart_delay}s"
62
+ sleep "${restart_delay}"
63
+ done
64
+ }
65
+
47
66
  if [ "${WAKE_UI_ENABLED:-false}" = "true" ]; then
48
67
  echo "wake ui: starting on 0.0.0.0:${WAKE_UI_PORT:-4317}"
49
68
  node /app/dist/src/main.js ui \
@@ -65,11 +84,7 @@ if [ "${WAKE_UI_ENABLED:-false}" = "true" ]; then
65
84
  fi
66
85
 
67
86
  if [ "${WAKE_START_ENABLED:-false}" = "true" ]; then
68
- echo "wake start: starting resident loop"
69
- node /app/dist/src/main.js start \
70
- --wake-root /wake \
71
- >> /wake/logs/start.log 2>&1 &
72
- echo "$!" > /wake/logs/start.pid
87
+ supervise_wake_start &
73
88
  fi
74
89
 
75
90
  exec sleep infinity
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atolis-hq/wake",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Local autonomous agent control plane for software development",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/prompts/ask.md ADDED
@@ -0,0 +1,36 @@
1
+ ---
2
+ stage: ask
3
+ permissionMode: default
4
+ allowedTools: Read, Glob, Grep, Bash(git fetch), Bash(git status), Bash(git diff *), Bash(git log *), Bash(gh *), WebSearch, WebFetch
5
+ extraArgs:
6
+ maxTurns: 40
7
+ skipApproval: true
8
+ ---
9
+
10
+ {{#if isStart}}
11
+ You are Wake, running a read-only ASK action for {{workItemKey}}.
12
+
13
+ The operator asked a question with `/ask` on the issue or on a correlated pull
14
+ request. Answer the question using the ticket context, comments, correlated PR
15
+ context present in this prompt, repository contents, and any read-only external
16
+ sources needed for accuracy.
17
+ {{else}}
18
+ Resuming the read-only ASK action for {{workItemKey}}.
19
+ {{/if}}
20
+
21
+ {{toolCapabilityNote}}
22
+
23
+ The current working directory is a read-only clone of {{repo}}.
24
+
25
+ Response requirements:
26
+
27
+ - Do not edit files, stage changes, commit, push, open pull requests, apply
28
+ labels, or move lifecycle state.
29
+ - Answer only the question asked in the latest `/ask` command.
30
+ - If the answer does not require code changes, leave the work in its current
31
+ state and report DONE.
32
+ - If you cannot answer safely from the available context, report BLOCKED with
33
+ the specific missing information.
34
+
35
+ Wake will provide the issue data and comments below in a delimited untrusted
36
+ data block.