@a5c-ai/babysitter-sdk 0.0.187-staging.38eafc19 → 0.0.187-staging.457841d2
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/dist/cli/commands/harnessPhase2.js +10 -10
- package/dist/hooks/dispatcher.d.ts +9 -0
- package/dist/hooks/dispatcher.d.ts.map +1 -1
- package/dist/hooks/dispatcher.js +94 -0
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -1
- package/dist/prompts/templates/intent-fidelity-checks.md +3 -1
- package/dist/prompts/templates/process-creation.md +9 -2
- package/dist/prompts/templates/process-guidelines.md +5 -2
- package/dist/prompts/templates/task-examples.md +47 -0
- package/dist/prompts/templates/task-kinds.md +44 -2
- package/dist/runtime/intrinsics/breakpoint.d.ts.map +1 -1
- package/dist/runtime/intrinsics/breakpoint.js +20 -1
- package/package.json +1 -1
|
@@ -126,22 +126,22 @@ async function resolveEffect(action, harnessName, options, piSession, discovered
|
|
|
126
126
|
if (piSession) {
|
|
127
127
|
const bashCommand = [command, ...shellArgs.map(harnessUtils_1.shellQuoteArg)].join(" ");
|
|
128
128
|
const bashResult = await piSession.executeBash(bashCommand);
|
|
129
|
+
const ok = bashResult.exitCode === 0;
|
|
129
130
|
return {
|
|
130
|
-
status:
|
|
131
|
-
value:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
: new Error(`Shell command exited with code ${bashResult.exitCode ?? "null"}: ${bashResult.output}`),
|
|
131
|
+
status: "ok",
|
|
132
|
+
value: ok
|
|
133
|
+
? bashResult.output
|
|
134
|
+
: { success: false, exitCode: bashResult.exitCode ?? 1, stdout: bashResult.output, stderr: "", error: `Shell command exited with code ${bashResult.exitCode ?? "null"}: ${bashResult.output}` },
|
|
135
135
|
stdout: bashResult.output,
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
const shellResult = await (0, harnessPhase1_1.execShellEffect)(command, shellArgs, cwd);
|
|
139
|
+
const shellOk = shellResult.exitCode === 0;
|
|
139
140
|
return {
|
|
140
|
-
status:
|
|
141
|
-
value:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
: new Error(`Shell command exited with code ${shellResult.exitCode}: ${shellResult.stderr}`),
|
|
141
|
+
status: "ok",
|
|
142
|
+
value: shellOk
|
|
143
|
+
? shellResult.stdout
|
|
144
|
+
: { success: false, exitCode: shellResult.exitCode, stdout: shellResult.stdout, stderr: shellResult.stderr, error: `Shell command exited with code ${shellResult.exitCode}: ${shellResult.stderr}` },
|
|
145
145
|
stdout: shellResult.stdout,
|
|
146
146
|
stderr: shellResult.stderr,
|
|
147
147
|
};
|
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
* Executes shell hooks from Node.js
|
|
4
4
|
*/
|
|
5
5
|
import type { HookDispatcherOptions, HookResult } from "./types";
|
|
6
|
+
export interface DiscoveredHook {
|
|
7
|
+
path: string;
|
|
8
|
+
name: string;
|
|
9
|
+
location: "per-repo" | "per-user";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Discover hook scripts for a given hook type from project and user directories.
|
|
13
|
+
*/
|
|
14
|
+
export declare function discoverHooks(hookType: string, cwd: string, homeDir?: string): Promise<DiscoveredHook[]>;
|
|
6
15
|
/**
|
|
7
16
|
* Find `plugins/babysitter/hooks/hook-dispatcher.sh` by walking up from cwd.
|
|
8
17
|
* This allows running from nested projects/fixtures inside a mono-repo.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/hooks/dispatcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/hooks/dispatcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EACV,qBAAqB,EACrB,UAAU,EAEX,MAAM,SAAS,CAAC;AAMjB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,UAAU,CAAC;CACnC;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAqB,GAC7B,OAAO,CAAC,cAAc,EAAE,CAAC,CAmC3B;AAkED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAetE;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,UAAU,CAAC,CAyHrB"}
|
package/dist/hooks/dispatcher.js
CHANGED
|
@@ -37,13 +37,102 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
};
|
|
38
38
|
})();
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.discoverHooks = discoverHooks;
|
|
40
41
|
exports.findHookDispatcherPath = findHookDispatcherPath;
|
|
41
42
|
exports.callHook = callHook;
|
|
42
43
|
const node_child_process_1 = require("node:child_process");
|
|
43
44
|
const node_fs_1 = require("node:fs");
|
|
45
|
+
const os = __importStar(require("node:os"));
|
|
44
46
|
const path = __importStar(require("node:path"));
|
|
45
47
|
const defaults_1 = require("../config/defaults");
|
|
46
48
|
const harness_1 = require("../harness");
|
|
49
|
+
const HOOK_SCRIPT_EXTENSIONS = new Set([".sh", ".js", ".ts", ".py", ".bash"]);
|
|
50
|
+
/**
|
|
51
|
+
* Discover hook scripts for a given hook type from project and user directories.
|
|
52
|
+
*/
|
|
53
|
+
async function discoverHooks(hookType, cwd, homeDir = os.homedir()) {
|
|
54
|
+
const projectHookDir = path.join(cwd, ".a5c", "hooks", hookType);
|
|
55
|
+
const userHookDir = path.join(homeDir, ".a5c", "hooks", hookType);
|
|
56
|
+
async function readHooksFromDir(dir, location) {
|
|
57
|
+
try {
|
|
58
|
+
const entries = await node_fs_1.promises.readdir(dir, { withFileTypes: true });
|
|
59
|
+
return entries
|
|
60
|
+
.filter((entry) => {
|
|
61
|
+
if (!entry.isFile())
|
|
62
|
+
return false;
|
|
63
|
+
const ext = path.extname(entry.name);
|
|
64
|
+
return HOOK_SCRIPT_EXTENSIONS.has(ext);
|
|
65
|
+
})
|
|
66
|
+
.map((entry) => ({
|
|
67
|
+
path: path.join(dir, entry.name),
|
|
68
|
+
name: entry.name,
|
|
69
|
+
location,
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
if (err.code === "ENOENT") {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const [projectHooks, userHooks] = await Promise.all([
|
|
80
|
+
readHooksFromDir(projectHookDir, "per-repo"),
|
|
81
|
+
readHooksFromDir(userHookDir, "per-user"),
|
|
82
|
+
]);
|
|
83
|
+
return [...projectHooks, ...userHooks];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Execute discovered hook scripts sequentially, passing payload as stdin JSON.
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
async function executeDiscoveredHooks(hooks, payload, hookType, cwd, timeout) {
|
|
90
|
+
const payloadJson = JSON.stringify(payload);
|
|
91
|
+
const executedHooks = [];
|
|
92
|
+
for (const hook of hooks) {
|
|
93
|
+
const result = await new Promise((resolve) => {
|
|
94
|
+
const child = (0, node_child_process_1.spawn)("bash", [hook.path], {
|
|
95
|
+
cwd,
|
|
96
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
97
|
+
timeout,
|
|
98
|
+
});
|
|
99
|
+
child.stdin.write(payloadJson);
|
|
100
|
+
child.stdin.end();
|
|
101
|
+
let timedOut = false;
|
|
102
|
+
const timeoutHandle = setTimeout(() => {
|
|
103
|
+
timedOut = true;
|
|
104
|
+
child.kill("SIGTERM");
|
|
105
|
+
}, timeout);
|
|
106
|
+
child.on("error", () => {
|
|
107
|
+
clearTimeout(timeoutHandle);
|
|
108
|
+
resolve({ success: false, exitCode: null });
|
|
109
|
+
});
|
|
110
|
+
child.on("close", (exitCode) => {
|
|
111
|
+
clearTimeout(timeoutHandle);
|
|
112
|
+
if (timedOut) {
|
|
113
|
+
resolve({ success: false, exitCode: null });
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
resolve({ success: exitCode === 0, exitCode });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
executedHooks.push({
|
|
121
|
+
hookPath: hook.path,
|
|
122
|
+
hookName: hook.name,
|
|
123
|
+
hookLocation: hook.location,
|
|
124
|
+
status: result.success ? "success" : "failed",
|
|
125
|
+
exitCode: result.exitCode ?? undefined,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const allSucceeded = executedHooks.every((h) => h.status === "success");
|
|
129
|
+
return {
|
|
130
|
+
hookType,
|
|
131
|
+
success: allSucceeded,
|
|
132
|
+
error: allSucceeded ? undefined : `One or more discovered hooks failed for ${hookType}`,
|
|
133
|
+
executedHooks,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
47
136
|
/**
|
|
48
137
|
* Find `plugins/babysitter/hooks/hook-dispatcher.sh` by walking up from cwd.
|
|
49
138
|
* This allows running from nested projects/fixtures inside a mono-repo.
|
|
@@ -78,6 +167,11 @@ async function callHook(options) {
|
|
|
78
167
|
const { hookType, payload, cwd = process.cwd(), timeout = defaults_1.DEFAULTS.hookTimeout, throwOnFailure = false, } = options;
|
|
79
168
|
const dispatcherPath = findHookDispatcherPath(cwd);
|
|
80
169
|
if (!dispatcherPath) {
|
|
170
|
+
// Fall back to filesystem-discovered hooks
|
|
171
|
+
const discoveredHooks = await discoverHooks(hookType, cwd);
|
|
172
|
+
if (discoveredHooks.length > 0) {
|
|
173
|
+
return executeDiscoveredHooks(discoveredHooks, payload, hookType, cwd, timeout);
|
|
174
|
+
}
|
|
81
175
|
return {
|
|
82
176
|
hookType,
|
|
83
177
|
success: false,
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Hook System API
|
|
3
3
|
* Public interface for calling hooks from process files
|
|
4
4
|
*/
|
|
5
|
-
export { callHook, } from "./dispatcher";
|
|
5
|
+
export { callHook, discoverHooks, } from "./dispatcher";
|
|
6
|
+
export type { DiscoveredHook } from "./dispatcher";
|
|
6
7
|
export type { HookType, HookResult, HookExecutionResult, HookPayload, HookDispatcherOptions, OnRunStartPayload, OnRunCompletePayload, OnRunFailPayload, OnTaskStartPayload, OnTaskCompletePayload, OnStepDispatchPayload, OnIterationStartPayload, OnIterationEndPayload, OnBreakpointPayload, PreCommitPayload, PreBranchPayload, PostPlanningPayload, OnScorePayload, } from "./types";
|
|
7
8
|
/**
|
|
8
9
|
* Helper function to create hook payloads with proper typing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,QAAQ,EACR,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,YAAY,EACV,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,GACf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,EAC9D,OAAO,EAAE,CAAC,GACT,CAAC,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAK3B"}
|
package/dist/hooks/index.js
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* Public interface for calling hooks from process files
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.callHook = void 0;
|
|
7
|
+
exports.discoverHooks = exports.callHook = void 0;
|
|
8
8
|
exports.createHookPayload = createHookPayload;
|
|
9
9
|
var dispatcher_1 = require("./dispatcher");
|
|
10
10
|
Object.defineProperty(exports, "callHook", { enumerable: true, get: function () { return dispatcher_1.callHook; } });
|
|
11
|
+
Object.defineProperty(exports, "discoverHooks", { enumerable: true, get: function () { return dispatcher_1.discoverHooks; } });
|
|
11
12
|
/**
|
|
12
13
|
* Helper function to create hook payloads with proper typing
|
|
13
14
|
*/
|
|
@@ -6,7 +6,9 @@ Before calling `run:create`, verify and document in your working notes:
|
|
|
6
6
|
2. The process structure follows library style/composition patterns rather than
|
|
7
7
|
a one-off minimal flow.
|
|
8
8
|
3. Quality gates exist (verification/refinement loops, integration checks,
|
|
9
|
-
and/or breakpoints appropriate for the task).
|
|
9
|
+
and/or breakpoints appropriate for the task). Objectively verifiable checks
|
|
10
|
+
(compilation, linting, tests, grep, dependencies) use `kind: 'shell'` with
|
|
11
|
+
`expectedExitCode`, not `kind: 'agent'`.
|
|
10
12
|
4. Any scope reduction, simplification, or recovery tradeoff is explicitly
|
|
11
13
|
approved by the user before execution.
|
|
12
14
|
|
|
@@ -55,7 +55,14 @@ After the process is created and before creating the run:
|
|
|
55
55
|
- wrong: skipping repo/process-library research before writing the process
|
|
56
56
|
- wrong: bypassing the orchestration model with helper scripts or inline logic
|
|
57
57
|
- wrong: using `kind: 'node'` in generated tasks
|
|
58
|
-
-
|
|
59
|
-
|
|
58
|
+
- wrong: using `kind: 'agent'` for deterministic verification (compilation,
|
|
59
|
+
linting, test execution, grep checks, dependency validation) -- these MUST
|
|
60
|
+
use `kind: 'shell'` with `expectedExitCode` for binary pass/fail enforcement
|
|
61
|
+
- correct: use `agent` or `skill` tasks for reasoning work (planning, code
|
|
62
|
+
review, architecture assessment, subjective evaluation)
|
|
63
|
+
- correct: use `shell` tasks for all objectively verifiable checks (tsc, eslint,
|
|
64
|
+
vitest, grep, dependency imports, runtime smoke tests)
|
|
60
65
|
- correct: include verification loops, refinement loops, quality gates, and
|
|
61
66
|
breakpoints where appropriate
|
|
67
|
+
- correct: pair shell verification gates with agent analysis tasks (shell runs
|
|
68
|
+
the check, agent interprets failures and suggests fixes)
|
|
@@ -75,8 +75,11 @@ specified:
|
|
|
75
75
|
- Integrate/link the main pages (or entry points) with functionality created
|
|
76
76
|
for every phase of the development process
|
|
77
77
|
- Quality gated iterative and convergent development/refinement loops
|
|
78
|
-
- Test driven -- where quality gates
|
|
79
|
-
|
|
78
|
+
- Test driven -- where quality gates use `kind: 'shell'` tasks with
|
|
79
|
+
`expectedExitCode` for deterministic verification (compilation, linting,
|
|
80
|
+
test suites, grep checks, dependency availability, runtime smoke tests).
|
|
81
|
+
Reserve `kind: 'agent'` for subjective assessment only (code review,
|
|
82
|
+
architecture evaluation, UX quality)
|
|
80
83
|
- Integration phases for each new functionality in every milestone
|
|
81
84
|
- Where relevant -- beautiful and polished UX with pixel-perfect verification
|
|
82
85
|
- Accurate and complete implementation of the user request
|
|
@@ -37,6 +37,53 @@ export const agentTask = defineTask('agent-scorer', (args, taskCtx) => ({
|
|
|
37
37
|
}));
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
### Shell Verification Task Example
|
|
41
|
+
|
|
42
|
+
Use shell tasks for all deterministic verification gates. Shell tasks produce
|
|
43
|
+
binary pass/fail results via exit codes that cannot be bypassed by agent
|
|
44
|
+
reasoning.
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
// Grep-based integration check
|
|
48
|
+
export const grepCheckTask = defineTask('grep-integration-check', (args, taskCtx) => ({
|
|
49
|
+
kind: 'shell',
|
|
50
|
+
title: 'Verify integration points exist',
|
|
51
|
+
shell: {
|
|
52
|
+
command: `cd ${args.projectDir || '.'} && grep -q "${args.pattern}" ${args.file}`,
|
|
53
|
+
expectedExitCode: 0,
|
|
54
|
+
timeout: 10000
|
|
55
|
+
},
|
|
56
|
+
io: {
|
|
57
|
+
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
|
|
58
|
+
outputJsonPath: `tasks/${taskCtx.effectId}/output.json`
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
// Multi-check verification gate
|
|
63
|
+
export const verificationGateTask = defineTask('verification-gate', (args, taskCtx) => ({
|
|
64
|
+
kind: 'shell',
|
|
65
|
+
title: 'Deterministic verification gate',
|
|
66
|
+
shell: {
|
|
67
|
+
command: [
|
|
68
|
+
`cd ${args.projectDir || '.'}`,
|
|
69
|
+
'npx tsc --noEmit',
|
|
70
|
+
'npx eslint src/ --max-warnings=0',
|
|
71
|
+
'npx vitest run --reporter=verbose'
|
|
72
|
+
].join(' && '),
|
|
73
|
+
expectedExitCode: 0,
|
|
74
|
+
timeout: 300000
|
|
75
|
+
},
|
|
76
|
+
io: {
|
|
77
|
+
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
|
|
78
|
+
outputJsonPath: `tasks/${taskCtx.effectId}/output.json`
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use `library/processes/shared/deterministic-quality-gate.js` for composable
|
|
84
|
+
preset gates (createGrepCheck, createCompilationGate, createTestSuiteGate,
|
|
85
|
+
createRuntimeSmokeTest).
|
|
86
|
+
|
|
40
87
|
### Skill Task Example
|
|
41
88
|
|
|
42
89
|
Important: Check which skills are actually available before assigning the skill
|
|
@@ -10,12 +10,54 @@ build tools), `breakpoint` (human approval), and `sleep` (time gates).
|
|
|
10
10
|
| Kind | Description | Executor | When to use |
|
|
11
11
|
|------|-------------|----------|-------------|
|
|
12
12
|
| ~~`node`~~ | ~~Node.js script~~ | ~~Local node process~~ | **NEVER -- forbidden. Convert to `agent` or `skill`.** |
|
|
13
|
-
| `shell` | Shell command | Local shell process |
|
|
14
|
-
| `agent` | LLM agent | Agent runtime | **Default for
|
|
13
|
+
| `shell` | Shell command | Local shell process | **Deterministic verification** (compilation, linting, tests, grep, dependency checks, smoke tests) AND running existing CLI tools, git, builds. The orchestrating agent must execute it intentionally and post the result |
|
|
14
|
+
| `agent` | LLM agent | Agent runtime | **Default for reasoning tasks** -- planning, implementation, analysis, scoring, debugging, code writing, research. Also for **subjective verification** (code review, architecture assessment, UX evaluation) |
|
|
15
15
|
| `skill` | {{skillSystemLabel}} | Skill system | When a matching installed skill exists (preferred over agent when available) |
|
|
16
16
|
| `breakpoint` | Human approval | UI/CLI | Decision gates requiring user input |
|
|
17
17
|
| `sleep` | Time gate | Scheduler | Time-based pauses |
|
|
18
18
|
|
|
19
|
+
### Shell vs Agent for Verification Tasks
|
|
20
|
+
|
|
21
|
+
**CRITICAL:** Verification tasks that produce deterministic, binary pass/fail
|
|
22
|
+
results MUST use `kind: 'shell'` with `expectedExitCode`, NOT `kind: 'agent'`.
|
|
23
|
+
Agent-interpreted verification creates soft gates where an LLM subjectively
|
|
24
|
+
assesses pass/fail -- this is unreliable for objectively testable checks.
|
|
25
|
+
|
|
26
|
+
| Verification type | Correct kind | Why |
|
|
27
|
+
|-------------------|-------------|-----|
|
|
28
|
+
| Compilation (tsc, gcc) | `shell` | Exit code is deterministic pass/fail |
|
|
29
|
+
| Linting (eslint, ruff) | `shell` | Exit code is deterministic pass/fail |
|
|
30
|
+
| Test suites (vitest, jest) | `shell` | Exit code is deterministic pass/fail |
|
|
31
|
+
| Grep/pattern checks | `shell` | Pattern exists or it doesn't |
|
|
32
|
+
| Dependency availability | `shell` | `node -e "require('pkg')"` succeeds or fails |
|
|
33
|
+
| Runtime smoke tests | `shell` | Start, curl, check status code, stop |
|
|
34
|
+
| Code review / design quality | `agent` | Requires subjective judgment |
|
|
35
|
+
| Architecture assessment | `agent` | Requires reasoning about tradeoffs |
|
|
36
|
+
| UX evaluation | `agent` | Subjective quality assessment |
|
|
37
|
+
|
|
38
|
+
**Anti-pattern (wrong):**
|
|
39
|
+
```javascript
|
|
40
|
+
// BAD: agent-interpreted verification -- the agent can say "looks good" without running anything
|
|
41
|
+
const verifyTask = defineTask('verify-compilation', (args, taskCtx) => ({
|
|
42
|
+
kind: 'agent',
|
|
43
|
+
title: 'Verify TypeScript compiles',
|
|
44
|
+
agent: { prompt: { instructions: ['Run tsc and check it passes'] } }
|
|
45
|
+
}));
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Correct pattern:**
|
|
49
|
+
```javascript
|
|
50
|
+
// GOOD: deterministic shell gate -- exit code cannot be negotiated
|
|
51
|
+
const verifyTask = defineTask('verify-compilation', (args, taskCtx) => ({
|
|
52
|
+
kind: 'shell',
|
|
53
|
+
title: 'TypeScript compilation check',
|
|
54
|
+
shell: { command: 'npx tsc --noEmit', expectedExitCode: 0, timeout: 120000 }
|
|
55
|
+
}));
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use `library/processes/shared/deterministic-quality-gate.js` for composable
|
|
59
|
+
preset gates (grep checks, compilation, test suites, runtime smoke tests).
|
|
60
|
+
|
|
19
61
|
### Effect Execution Hints
|
|
20
62
|
|
|
21
63
|
Tasks can include an `execution` field to express preferences about how the effect should be executed:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"breakpoint.d.ts","sourceRoot":"","sources":["../../../src/runtime/intrinsics/breakpoint.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"breakpoint.d.ts","sourceRoot":"","sources":["../../../src/runtime/intrinsics/breakpoint.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAmC,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC1H,OAAO,EAAoB,oBAAoB,EAAE,MAAM,QAAQ,CAAC;AAkChE,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,OAAO,EAChD,OAAO,EAAE,CAAC,EACV,OAAO,EAAE,oBAAoB,EAC7B,OAAO,CAAC,EAAE,iBAAiB,GAAG,wBAAwB,GACrD,OAAO,CAAC,gBAAgB,CAAC,CAiD3B"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.runBreakpointIntrinsic = runBreakpointIntrinsic;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
4
9
|
const task_1 = require("./task");
|
|
5
10
|
const journal_1 = require("../../storage/journal");
|
|
6
11
|
const BREAKPOINT_TASK_ID = "__sdk.breakpoint";
|
|
@@ -28,10 +33,24 @@ function runBreakpointIntrinsic(payload, context, options) {
|
|
|
28
33
|
const ctx = context;
|
|
29
34
|
if (ctx.nonInteractive) {
|
|
30
35
|
const bpLabel = options?.label ?? "unnamed";
|
|
36
|
+
let logSeq;
|
|
37
|
+
if (ctx.logSeq !== undefined && ctx.recordedLogSeqs !== undefined) {
|
|
38
|
+
logSeq = ++ctx.logSeq;
|
|
39
|
+
if (ctx.recordedLogSeqs.has(logSeq)) {
|
|
40
|
+
return Promise.resolve({ approved: true, response: "Auto-approved (non-interactive mode)" });
|
|
41
|
+
}
|
|
42
|
+
ctx.recordedLogSeqs.add(logSeq);
|
|
43
|
+
void node_fs_1.promises.appendFile(node_path_1.default.join(context.runDir, "state", "logSeqs.txt"), `${logSeq}\n`).catch(() => {
|
|
44
|
+
// Never let logging break orchestration.
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
logSeq = -1;
|
|
49
|
+
}
|
|
31
50
|
void (0, journal_1.appendEvent)({
|
|
32
51
|
runDir: context.runDir,
|
|
33
52
|
eventType: "PROCESS_LOG",
|
|
34
|
-
event: { logSeq
|
|
53
|
+
event: { logSeq, label: "breakpoint:skipped", message: `Breakpoint '${bpLabel}' auto-approved (non-interactive mode)` },
|
|
35
54
|
}).catch(() => {
|
|
36
55
|
// Never let logging break orchestration.
|
|
37
56
|
});
|
package/package.json
CHANGED