@atolis-hq/wake 0.1.0
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.
- package/LICENSE +201 -0
- package/README.md +140 -0
- package/dist/src/adapters/claude/claude-runner.js +388 -0
- package/dist/src/adapters/claude/prompt-templates.js +57 -0
- package/dist/src/adapters/codex/codex-runner.js +391 -0
- package/dist/src/adapters/cursor/cursor-runner.js +352 -0
- package/dist/src/adapters/docker/docker-cli.js +97 -0
- package/dist/src/adapters/fake/fake-artifact-verifier.js +14 -0
- package/dist/src/adapters/fake/fake-github-pull-request-activity-source.js +73 -0
- package/dist/src/adapters/fake/fake-resource-index.js +21 -0
- package/dist/src/adapters/fake/fake-runner.js +22 -0
- package/dist/src/adapters/fake/fake-ticketing-system.js +166 -0
- package/dist/src/adapters/fake/fake-workspace-manager.js +27 -0
- package/dist/src/adapters/fs/resource-index.js +84 -0
- package/dist/src/adapters/fs/self-update-ledger.js +17 -0
- package/dist/src/adapters/fs/state-store.js +364 -0
- package/dist/src/adapters/git/git-workspace-manager.js +168 -0
- package/dist/src/adapters/github/github-artifact-verifier.js +38 -0
- package/dist/src/adapters/github/github-auth.js +16 -0
- package/dist/src/adapters/github/github-client.js +100 -0
- package/dist/src/adapters/github/github-issues-work-source.js +410 -0
- package/dist/src/adapters/github/github-pull-request-activity-source.js +263 -0
- package/dist/src/adapters/http/ui-assets.js +302 -0
- package/dist/src/adapters/http/ui-data.js +389 -0
- package/dist/src/adapters/http/ui-server.js +151 -0
- package/dist/src/adapters/runner/cli-command.js +43 -0
- package/dist/src/adapters/runner/prompt-templates.js +76 -0
- package/dist/src/adapters/runner/runner-cli-adapter.js +112 -0
- package/dist/src/adapters/runner/runner-registry.js +141 -0
- package/dist/src/adapters/runner/stage-prompt.js +256 -0
- package/dist/src/adapters/runner/transcripts.js +25 -0
- package/dist/src/cli/correlate-command.js +62 -0
- package/dist/src/cli/init-command.js +11 -0
- package/dist/src/cli/locks-command.js +19 -0
- package/dist/src/cli/sandbox-command.js +191 -0
- package/dist/src/cli/sandbox-logging.js +30 -0
- package/dist/src/cli/sandbox-resume.js +77 -0
- package/dist/src/cli/scaffold-assets.js +173 -0
- package/dist/src/cli/self-update-command.js +158 -0
- package/dist/src/cli/startup-preflight.js +127 -0
- package/dist/src/cli/stop-command.js +42 -0
- package/dist/src/cli/ui-command.js +27 -0
- package/dist/src/config/defaults.js +11 -0
- package/dist/src/config/load-config.js +26 -0
- package/dist/src/core/contracts.js +1 -0
- package/dist/src/core/control-plane.js +127 -0
- package/dist/src/core/lifecycle-service.js +8 -0
- package/dist/src/core/policy-engine.js +200 -0
- package/dist/src/core/projection-updater.js +438 -0
- package/dist/src/core/quota-backoff.js +69 -0
- package/dist/src/core/sink-router.js +85 -0
- package/dist/src/core/tick-runner.js +1347 -0
- package/dist/src/domain/branch-naming.js +14 -0
- package/dist/src/domain/resource-uri.js +38 -0
- package/dist/src/domain/runner-routing.js +108 -0
- package/dist/src/domain/schema.js +685 -0
- package/dist/src/domain/sources.js +16 -0
- package/dist/src/domain/stages.js +39 -0
- package/dist/src/domain/types.js +1 -0
- package/dist/src/domain/workflows.js +83 -0
- package/dist/src/lib/clock.js +5 -0
- package/dist/src/lib/event-log.js +21 -0
- package/dist/src/lib/json-file.js +23 -0
- package/dist/src/lib/lock.js +118 -0
- package/dist/src/lib/paths.js +44 -0
- package/dist/src/lib/work-id.js +19 -0
- package/dist/src/main.js +523 -0
- package/dist/src/version.js +1 -0
- package/docker/Dockerfile +54 -0
- package/docker/entrypoint.sh +75 -0
- package/docker/log-command.sh +107 -0
- package/docker/setup.sh +72 -0
- package/package.json +52 -0
- package/prompts/implement.md +49 -0
- package/prompts/refine.md +44 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wake's branch-naming convention for a work item's implementation branch,
|
|
3
|
+
* keyed on the originating ticket's issue number (spec D2 — human-readable,
|
|
4
|
+
* not the opaque work id).
|
|
5
|
+
*
|
|
6
|
+
* Lives in domain/ (not the git adapter) because it is pure vocabulary with
|
|
7
|
+
* no IO: `core/` needs it to compute the branch a run should have pushed to
|
|
8
|
+
* when verifying agent-reported artifacts, and `core/` may import domain/
|
|
9
|
+
* but never a concrete adapter directly. `adapters/git/git-workspace-manager.ts`
|
|
10
|
+
* re-exports this for its own callers so existing imports keep working.
|
|
11
|
+
*/
|
|
12
|
+
export function branchNameForIssue(issueNumber) {
|
|
13
|
+
return `wake/issue-${issueNumber}`;
|
|
14
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Resource URI grammar: `<provider>:<kind>:<locator>`.
|
|
4
|
+
*
|
|
5
|
+
* `provider` matches the adapter's registered source/sink name; `kind` uses
|
|
6
|
+
* the provider's native vocabulary (`github:pr:…` but `gitlab:mr:…`). The
|
|
7
|
+
* locator grammar is provider-specific and opaque to core — everything after
|
|
8
|
+
* the second colon is matched as a single blob and never inspected here.
|
|
9
|
+
* Core compares URIs for equality only (ADR 0001 §1).
|
|
10
|
+
*/
|
|
11
|
+
const RESOURCE_URI_PATTERN = /^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*:.+$/;
|
|
12
|
+
export const resourceUriSchema = z
|
|
13
|
+
.string()
|
|
14
|
+
.regex(RESOURCE_URI_PATTERN, 'must match <provider>:<kind>:<locator>');
|
|
15
|
+
export function buildResourceUri(provider, kind, locator) {
|
|
16
|
+
return resourceUriSchema.parse(`${provider}:${kind}:${locator}`);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Wake-owned relationship vocabulary — the graph edge type, deliberately
|
|
20
|
+
* independent of the URI's provider-native `kind`. A new provider adds URI
|
|
21
|
+
* kinds, never new roles; a new role is a Wake modelling decision.
|
|
22
|
+
*/
|
|
23
|
+
export const correlationRoleSchema = z.enum([
|
|
24
|
+
'representation',
|
|
25
|
+
'implementation',
|
|
26
|
+
'discussion',
|
|
27
|
+
'review',
|
|
28
|
+
'documentation',
|
|
29
|
+
'decision',
|
|
30
|
+
]);
|
|
31
|
+
/** Exactly one work item may hold `primary` per resource URI (ADR 0001 §2). */
|
|
32
|
+
export const correlationRelationSchema = z.enum(['primary', 'secondary']);
|
|
33
|
+
export const correlationProvenanceSchema = z.enum([
|
|
34
|
+
'wake-created',
|
|
35
|
+
'agent-reported',
|
|
36
|
+
'detected',
|
|
37
|
+
'operator-declared',
|
|
38
|
+
]);
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { defaultWorkflowName } from './workflows.js';
|
|
2
|
+
export function maxConfiguredRunnerTimeoutMs(config) {
|
|
3
|
+
const activeRunnerNames = new Set(Object.values(config.tiers).flatMap((candidates) => candidates));
|
|
4
|
+
for (const workflow of Object.values(config.workflows)) {
|
|
5
|
+
for (const stageRoute of Object.values(workflow.stages)) {
|
|
6
|
+
if (stageRoute.runner !== undefined) {
|
|
7
|
+
activeRunnerNames.add(stageRoute.runner);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
for (const stageRoute of Object.values(config.stages)) {
|
|
12
|
+
if (stageRoute.runner !== undefined) {
|
|
13
|
+
activeRunnerNames.add(stageRoute.runner);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const registryTimeouts = [...activeRunnerNames]
|
|
17
|
+
.map((name) => config.runners[name])
|
|
18
|
+
.map((entry) => entry === undefined || entry.kind === 'fake' ? undefined : entry.timeoutMs)
|
|
19
|
+
.filter((timeout) => timeout !== undefined);
|
|
20
|
+
return registryTimeouts.length > 0 ? Math.max(...registryTimeouts) : Infinity;
|
|
21
|
+
}
|
|
22
|
+
// How often an 'estimated' pause (exponential-backoff guess, not a reset time
|
|
23
|
+
// the CLI actually reported) gets a recovery probe: a real attempt let through
|
|
24
|
+
// early in case the guess overshot and quota actually reset sooner. 'reported'
|
|
25
|
+
// pauses (an actual reset time) are trusted for their full duration - no
|
|
26
|
+
// probing needed since we already know when they clear.
|
|
27
|
+
const recoveryProbeIntervalMs = 15 * 60_000;
|
|
28
|
+
function isRunnerPaused(input) {
|
|
29
|
+
const health = input.ledger?.runners?.[input.runnerName];
|
|
30
|
+
const pausedUntil = health?.pausedUntil;
|
|
31
|
+
if (pausedUntil === undefined || Date.parse(pausedUntil) <= input.now.getTime()) {
|
|
32
|
+
return { paused: false, isProbe: false };
|
|
33
|
+
}
|
|
34
|
+
if (health?.pausedUntilSource === 'estimated' && health.lastFailureAt !== undefined) {
|
|
35
|
+
const sinceLastFailureMs = input.now.getTime() - Date.parse(health.lastFailureAt);
|
|
36
|
+
if (sinceLastFailureMs >= recoveryProbeIntervalMs) {
|
|
37
|
+
return { paused: false, isProbe: true };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { paused: true, isProbe: false };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolves which named runner should execute a stage/action.
|
|
44
|
+
*
|
|
45
|
+
* A stage pinned directly to a runner (`stages[stage].runner`) has no
|
|
46
|
+
* fallback list, so it always returns that runner regardless of health.
|
|
47
|
+
* A stage routed through a `tier` walks the tier's ordered candidates and
|
|
48
|
+
* skips any currently quota-paused per the ledger (#67 sideways fallback),
|
|
49
|
+
* returning `null` only when every candidate in the tier is paused - callers
|
|
50
|
+
* should treat that as "nothing to do this tick", not a config error. Once a
|
|
51
|
+
* higher-priority candidate's pause expires, it is preferred again on the
|
|
52
|
+
* next call, which is the "rotation" back to the primary runner.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveRunnerRouting(input) {
|
|
55
|
+
const workflowName = input.workflowName ?? defaultWorkflowName(input.config);
|
|
56
|
+
const workflow = input.config.workflows[workflowName];
|
|
57
|
+
if (workflow === undefined) {
|
|
58
|
+
throw new Error(`Unknown workflow "${workflowName}".`);
|
|
59
|
+
}
|
|
60
|
+
const stageRoute = workflow.stages[input.stage];
|
|
61
|
+
if (stageRoute?.runner !== undefined) {
|
|
62
|
+
const entry = input.config.runners[stageRoute.runner];
|
|
63
|
+
if (entry === undefined) {
|
|
64
|
+
throw new Error(`Stage ${input.stage} pins unknown runner "${stageRoute.runner}".`);
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
runnerName: stageRoute.runner,
|
|
68
|
+
runnerKind: entry.kind,
|
|
69
|
+
...(stageRoute.tier === undefined ? {} : { tier: stageRoute.tier }),
|
|
70
|
+
reason: `stage ${input.stage} pins runner ${stageRoute.runner}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const tier = stageRoute?.tier ?? input.config.defaultTier;
|
|
74
|
+
const candidates = input.config.tiers[tier];
|
|
75
|
+
if (candidates === undefined || candidates.length === 0) {
|
|
76
|
+
throw new Error(`Stage ${input.stage} routes to unknown or empty tier "${tier}".`);
|
|
77
|
+
}
|
|
78
|
+
const now = input.now ?? new Date();
|
|
79
|
+
let selected;
|
|
80
|
+
for (const candidate of candidates) {
|
|
81
|
+
const status = isRunnerPaused({ runnerName: candidate, ledger: input.ledger, now });
|
|
82
|
+
if (!status.paused) {
|
|
83
|
+
selected = { runnerName: candidate, isProbe: status.isProbe };
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (selected === undefined) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const { runnerName, isProbe } = selected;
|
|
91
|
+
const entry = input.config.runners[runnerName];
|
|
92
|
+
if (entry === undefined) {
|
|
93
|
+
throw new Error(`Tier ${tier} references unknown runner "${runnerName}".`);
|
|
94
|
+
}
|
|
95
|
+
const isFallback = runnerName !== candidates[0];
|
|
96
|
+
return {
|
|
97
|
+
runnerName,
|
|
98
|
+
runnerKind: entry.kind,
|
|
99
|
+
tier,
|
|
100
|
+
reason: isProbe
|
|
101
|
+
? `tier ${tier} recovery probe on ${runnerName} (estimated pause not yet elapsed)`
|
|
102
|
+
: isFallback
|
|
103
|
+
? `tier ${tier} fell back to ${runnerName} (higher-priority candidates paused)`
|
|
104
|
+
: stageRoute?.tier === undefined
|
|
105
|
+
? `defaultTier ${tier} selected runner ${runnerName}`
|
|
106
|
+
: `stage ${input.stage} tier ${tier} selected runner ${runnerName}`,
|
|
107
|
+
};
|
|
108
|
+
}
|