@a5c-ai/babysitter-sdk 0.0.186 → 0.0.187-staging.02a0ee21
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/costStats.d.ts +13 -0
- package/dist/cli/commands/costStats.d.ts.map +1 -0
- package/dist/cli/commands/costStats.js +319 -0
- package/dist/cli/commands/harnessPhase2.js +10 -10
- package/dist/cli/commands/instructions.d.ts.map +1 -1
- package/dist/cli/commands/instructions.js +5 -3
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +16 -1
- package/dist/config/defaults.d.ts +10 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +19 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -1
- package/dist/cost/claudeCodeParser.d.ts +81 -0
- package/dist/cost/claudeCodeParser.d.ts.map +1 -0
- package/dist/cost/claudeCodeParser.js +232 -0
- package/dist/cost/collector.d.ts +42 -0
- package/dist/cost/collector.d.ts.map +1 -0
- package/dist/cost/collector.js +105 -0
- package/dist/cost/index.d.ts +18 -0
- package/dist/cost/index.d.ts.map +1 -0
- package/dist/cost/index.js +37 -0
- package/dist/cost/journal.d.ts +40 -0
- package/dist/cost/journal.d.ts.map +1 -0
- package/dist/cost/journal.js +137 -0
- package/dist/cost/types.d.ts +164 -0
- package/dist/cost/types.d.ts.map +1 -0
- package/dist/cost/types.js +231 -0
- package/dist/harness/claudeCode.js +1 -1
- package/dist/harness/cursor.js +1 -1
- package/dist/harness/discovery.d.ts.map +1 -1
- package/dist/harness/discovery.js +8 -1
- package/dist/harness/geminiCli.js +1 -1
- package/dist/harness/githubCopilot.js +1 -1
- package/dist/harness/index.d.ts +1 -0
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +3 -1
- package/dist/harness/invoker.d.ts.map +1 -1
- package/dist/harness/invoker.js +1 -0
- package/dist/harness/openclaw.d.ts +20 -0
- package/dist/harness/openclaw.d.ts.map +1 -0
- package/dist/harness/openclaw.js +338 -0
- package/dist/harness/opencode.d.ts.map +1 -1
- package/dist/harness/opencode.js +75 -5
- package/dist/harness/registry.d.ts.map +1 -1
- package/dist/harness/registry.js +2 -0
- 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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/logging/runLogger.js +2 -2
- package/dist/prompts/babysitterMdDiscovery.d.ts +38 -0
- package/dist/prompts/babysitterMdDiscovery.d.ts.map +1 -0
- package/dist/prompts/babysitterMdDiscovery.js +150 -0
- package/dist/prompts/compose.d.ts.map +1 -1
- package/dist/prompts/compose.js +5 -0
- package/dist/prompts/context.d.ts +4 -0
- package/dist/prompts/context.d.ts.map +1 -1
- package/dist/prompts/context.js +37 -0
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +2 -1
- package/dist/prompts/parts/index.d.ts +3 -0
- package/dist/prompts/parts/index.d.ts.map +1 -1
- package/dist/prompts/parts/index.js +7 -1
- package/dist/prompts/parts/parallelPhaseDetection.d.ts +6 -0
- package/dist/prompts/parts/parallelPhaseDetection.d.ts.map +1 -0
- package/dist/prompts/parts/parallelPhaseDetection.js +10 -0
- package/dist/prompts/parts/projectInstructions.d.ts +8 -0
- package/dist/prompts/parts/projectInstructions.d.ts.map +1 -0
- package/dist/prompts/parts/projectInstructions.js +28 -0
- package/dist/prompts/parts/runOverlapDetection.d.ts +6 -0
- package/dist/prompts/parts/runOverlapDetection.d.ts.map +1 -0
- package/dist/prompts/parts/runOverlapDetection.js +10 -0
- package/dist/prompts/templates/commands/doctor.md +9 -9
- package/dist/prompts/templates/intent-fidelity-checks.md +3 -1
- package/dist/prompts/templates/parallel-phase-detection.md +59 -0
- package/dist/prompts/templates/process-creation.md +9 -2
- package/dist/prompts/templates/process-guidelines.md +5 -2
- package/dist/prompts/templates/run-overlap-detection.md +63 -0
- 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/dist/runtime/orchestrateIteration.d.ts.map +1 -1
- package/dist/runtime/orchestrateIteration.js +14 -0
- package/dist/session/types.d.ts +2 -0
- package/dist/session/types.d.ts.map +1 -1
- package/dist/storage/createRunDir.d.ts.map +1 -1
- package/dist/storage/createRunDir.js +2 -0
- package/dist/storage/icloudWarning.d.ts +6 -0
- package/dist/storage/icloudWarning.d.ts.map +1 -0
- package/dist/storage/icloudWarning.js +84 -0
- package/dist/storage/journal.d.ts.map +1 -1
- package/dist/storage/journal.js +2 -0
- package/package.json +1 -1
|
@@ -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
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
### 2.0 Run Overlap Detection
|
|
2
|
+
|
|
3
|
+
Before calling `$CLI run:create`, scan for recent runs that may already cover
|
|
4
|
+
the same work. This avoids creating duplicate runs that waste tokens and
|
|
5
|
+
complicate the run history.
|
|
6
|
+
|
|
7
|
+
**Scan procedure:**
|
|
8
|
+
|
|
9
|
+
1. List recent run directories:
|
|
10
|
+
```bash
|
|
11
|
+
ls -lt .a5c/runs/ | head -20
|
|
12
|
+
```
|
|
13
|
+
2. For each run created within the last 30 minutes, read its metadata:
|
|
14
|
+
```bash
|
|
15
|
+
cat .a5c/runs/<runId>/run.json
|
|
16
|
+
```
|
|
17
|
+
3. Compare the candidate run's `prompt` and `processId` against the new
|
|
18
|
+
request:
|
|
19
|
+
- **Keyword overlap**: does the existing prompt share 3 or more significant
|
|
20
|
+
keywords with the new prompt?
|
|
21
|
+
- **Process match**: does `processId` match the process you intend to use?
|
|
22
|
+
4. If overlap is detected, check its current status:
|
|
23
|
+
```bash
|
|
24
|
+
$CLI run:status .a5c/runs/<runId>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**If an overlapping run is found:**
|
|
28
|
+
|
|
29
|
+
{{#interactive}}
|
|
30
|
+
Surface a clear warning to the user with the similar run's details:
|
|
31
|
+
|
|
32
|
+
- Run ID: `<runId>`
|
|
33
|
+
- Process: `<processId>`
|
|
34
|
+
- Created: `<createdAt>`
|
|
35
|
+
- Status: `<status>`
|
|
36
|
+
- Prompt excerpt: `<first 120 chars of prompt>`
|
|
37
|
+
|
|
38
|
+
Ask the user to choose one of:
|
|
39
|
+
1. **Proceed** — create a new run anyway (e.g., intentional retry or variant)
|
|
40
|
+
2. **Skip** — abort run creation; the existing run already covers the request
|
|
41
|
+
3. **Resume** — resume the existing run via `$CLI run:iterate .a5c/runs/<runId>`
|
|
42
|
+
|
|
43
|
+
Do not proceed with `run:create` until the user has responded.
|
|
44
|
+
{{/interactive}}
|
|
45
|
+
{{^interactive}}
|
|
46
|
+
Log a warning with the overlapping run's details (runId, processId, createdAt,
|
|
47
|
+
status) and continue creating the new run. Non-interactive mode does not block
|
|
48
|
+
on overlap — the caller is responsible for deduplication.
|
|
49
|
+
{{/interactive}}
|
|
50
|
+
|
|
51
|
+
**No overlap found:** proceed directly to `$CLI run:create` as normal.
|
|
52
|
+
|
|
53
|
+
**Tracking run chains:**
|
|
54
|
+
|
|
55
|
+
When creating a run that intentionally follows or retries a previous run,
|
|
56
|
+
include the related run ID in the inputs so the process can reference it:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"relatedRunId": "<previousRunId>",
|
|
61
|
+
"relatedRunReason": "retry|variant|follow-up"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
@@ -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
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrateIteration.d.ts","sourceRoot":"","sources":["../../src/runtime/orchestrateIteration.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"orchestrateIteration.d.ts","sourceRoot":"","sources":["../../src/runtime/orchestrateIteration.ts"],"names":[],"mappings":"AAeA,OAAO,EAEL,eAAe,EACf,kBAAkB,EAInB,MAAM,SAAS,CAAC;AAgBjB,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC,CAmJhG"}
|
|
@@ -41,6 +41,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
41
41
|
const url_1 = require("url");
|
|
42
42
|
const journal_1 = require("../storage/journal");
|
|
43
43
|
const runFiles_1 = require("../storage/runFiles");
|
|
44
|
+
const journal_2 = require("../cost/journal");
|
|
44
45
|
const lock_1 = require("../storage/lock");
|
|
45
46
|
const createReplayEngine_1 = require("./replay/createReplayEngine");
|
|
46
47
|
const stateCache_1 = require("./replay/stateCache");
|
|
@@ -86,11 +87,24 @@ async function orchestrateIteration(options) {
|
|
|
86
87
|
try {
|
|
87
88
|
const output = await (0, processContext_1.withProcessContext)(engine.internalContext, () => processFn(inputs, engine.context, options.context));
|
|
88
89
|
const outputRef = await (0, runFiles_1.writeRunOutput)(options.runDir, output);
|
|
90
|
+
// Compute cost stats for run completion
|
|
91
|
+
let costStats = undefined;
|
|
92
|
+
try {
|
|
93
|
+
const journalEvents = await (0, journal_1.loadJournal)(options.runDir);
|
|
94
|
+
const costEvents = (0, journal_2.extractCostEvents)(journalEvents);
|
|
95
|
+
if (costEvents.length > 0) {
|
|
96
|
+
costStats = (0, journal_2.computeRunCostStats)(engine.runId, journalEvents);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Cost stats are optional - don't fail the run
|
|
101
|
+
}
|
|
89
102
|
await (0, journal_1.appendEvent)({
|
|
90
103
|
runDir: options.runDir,
|
|
91
104
|
eventType: "RUN_COMPLETED",
|
|
92
105
|
event: {
|
|
93
106
|
outputRef,
|
|
107
|
+
costStats,
|
|
94
108
|
},
|
|
95
109
|
});
|
|
96
110
|
// Rebuild state cache so it includes the terminal event.
|
package/dist/session/types.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface SessionState {
|
|
|
20
20
|
lastIterationAt: string;
|
|
21
21
|
/** Array of recent iteration durations in seconds (last 3) */
|
|
22
22
|
iterationTimes: number[];
|
|
23
|
+
/** Optional key-value metadata (e.g. external correlation IDs) */
|
|
24
|
+
metadata?: Record<string, string>;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Complete session file content including state and prompt.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/session/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,MAAM,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,eAAe,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,cAAc,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/session/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createRunDir.d.ts","sourceRoot":"","sources":["../../src/storage/createRunDir.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAyB,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"createRunDir.d.ts","sourceRoot":"","sources":["../../src/storage/createRunDir.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAyB,WAAW,EAAE,MAAM,SAAS,CAAC;AAmBlF,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB;;;GAqC9D"}
|
|
@@ -9,9 +9,11 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const paths_1 = require("./paths");
|
|
10
10
|
const atomic_1 = require("./atomic");
|
|
11
11
|
const clock_1 = require("./clock");
|
|
12
|
+
const icloudWarning_1 = require("./icloudWarning");
|
|
12
13
|
const GITIGNORE_CONTENT = `state/\ntasks/*/artifacts/\nblobs/\norphaned/\n`;
|
|
13
14
|
async function createRunDir(options) {
|
|
14
15
|
const runDir = (0, paths_1.getRunDir)(options.runsRoot, options.runId);
|
|
16
|
+
await (0, icloudWarning_1.warnIfICloudDrivePath)(runDir);
|
|
15
17
|
await fs_1.promises.mkdir(runDir, { recursive: true });
|
|
16
18
|
await Promise.all([
|
|
17
19
|
fs_1.promises.mkdir((0, paths_1.getJournalDir)(runDir), { recursive: true }),
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type WarningOutput = Pick<NodeJS.WriteStream, "write">;
|
|
2
|
+
export declare function detectICloudDrivePath(inputPath: string): Promise<string | undefined>;
|
|
3
|
+
export declare function warnIfICloudDrivePath(inputPath: string, output?: WarningOutput): Promise<boolean>;
|
|
4
|
+
export declare function __resetICloudDriveWarningCacheForTests(): void;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=icloudWarning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icloudWarning.d.ts","sourceRoot":"","sources":["../../src/storage/icloudWarning.ts"],"names":[],"mappings":"AAMA,KAAK,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AA0CvD,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAS1F;AAED,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,aAA8B,GACrC,OAAO,CAAC,OAAO,CAAC,CAuBlB;AAED,wBAAgB,sCAAsC,IAAI,IAAI,CAE7D"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.detectICloudDrivePath = detectICloudDrivePath;
|
|
7
|
+
exports.warnIfICloudDrivePath = warnIfICloudDrivePath;
|
|
8
|
+
exports.__resetICloudDriveWarningCacheForTests = __resetICloudDriveWarningCacheForTests;
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const ICLOUD_DRIVE_MARKER = "/Library/Mobile Documents/com~apple~CloudDocs";
|
|
12
|
+
const warnedPaths = new Set();
|
|
13
|
+
function normalizePathForMatch(inputPath) {
|
|
14
|
+
return inputPath.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
15
|
+
}
|
|
16
|
+
function getICloudWarningKey(candidatePath) {
|
|
17
|
+
const normalized = normalizePathForMatch(candidatePath);
|
|
18
|
+
const markerIndex = normalized.indexOf(ICLOUD_DRIVE_MARKER);
|
|
19
|
+
if (markerIndex === -1) {
|
|
20
|
+
return normalized;
|
|
21
|
+
}
|
|
22
|
+
return normalized.slice(0, markerIndex + ICLOUD_DRIVE_MARKER.length);
|
|
23
|
+
}
|
|
24
|
+
function isICloudDrivePath(candidatePath) {
|
|
25
|
+
const normalized = normalizePathForMatch(candidatePath);
|
|
26
|
+
return normalized === ICLOUD_DRIVE_MARKER || normalized.includes(`${ICLOUD_DRIVE_MARKER}/`);
|
|
27
|
+
}
|
|
28
|
+
async function resolveExistingAncestorRealPath(inputPath) {
|
|
29
|
+
let current = path_1.default.resolve(inputPath);
|
|
30
|
+
let reachedFilesystemRoot = false;
|
|
31
|
+
while (!reachedFilesystemRoot) {
|
|
32
|
+
try {
|
|
33
|
+
return await fs_1.promises.realpath(current);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const err = error;
|
|
37
|
+
if (err.code !== "ENOENT") {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const parent = path_1.default.dirname(current);
|
|
41
|
+
if (parent === current) {
|
|
42
|
+
reachedFilesystemRoot = true;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
current = parent;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
async function detectICloudDrivePath(inputPath) {
|
|
52
|
+
const realAncestor = await resolveExistingAncestorRealPath(inputPath);
|
|
53
|
+
const candidates = [
|
|
54
|
+
realAncestor,
|
|
55
|
+
path_1.default.resolve(inputPath),
|
|
56
|
+
inputPath,
|
|
57
|
+
].filter((candidate) => typeof candidate === "string" && candidate.trim() !== "");
|
|
58
|
+
return candidates.find((candidate) => isICloudDrivePath(candidate));
|
|
59
|
+
}
|
|
60
|
+
async function warnIfICloudDrivePath(inputPath, output = process.stderr) {
|
|
61
|
+
const matchedPath = await detectICloudDrivePath(inputPath);
|
|
62
|
+
if (!matchedPath) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const dedupeKey = getICloudWarningKey(matchedPath);
|
|
66
|
+
if (warnedPaths.has(dedupeKey)) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
warnedPaths.add(dedupeKey);
|
|
70
|
+
const message = [
|
|
71
|
+
"",
|
|
72
|
+
"!!! WARNING: Babysitter state is inside iCloud Drive.",
|
|
73
|
+
`Path: ${path_1.default.resolve(inputPath)}`,
|
|
74
|
+
"iCloud sync can race .a5c journal writes and produce duplicate or conflicting journal events.",
|
|
75
|
+
"Recommendation: move the project or set BABYSITTER_STATE_DIR/BABYSITTER_RUNS_DIR outside iCloud Drive.",
|
|
76
|
+
"Known issue: github.com/a5c-ai/babysitter/issues/77",
|
|
77
|
+
"",
|
|
78
|
+
].join("\n");
|
|
79
|
+
output.write(`${message}\n`);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
function __resetICloudDriveWarningCacheForTests() {
|
|
83
|
+
warnedPaths.clear();
|
|
84
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../src/storage/journal.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,YAAY,EAAc,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../src/storage/journal.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,YAAY,EAAc,MAAM,SAAS,CAAC;AAwB1F,wBAAsB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwBtF;AA2BD,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA6BzE"}
|
package/dist/storage/journal.js
CHANGED
|
@@ -12,6 +12,7 @@ const paths_1 = require("./paths");
|
|
|
12
12
|
const atomic_1 = require("./atomic");
|
|
13
13
|
const ulids_1 = require("./ulids");
|
|
14
14
|
const clock_1 = require("./clock");
|
|
15
|
+
const icloudWarning_1 = require("./icloudWarning");
|
|
15
16
|
function formatSeq(seq) {
|
|
16
17
|
return seq.toString().padStart(6, "0");
|
|
17
18
|
}
|
|
@@ -31,6 +32,7 @@ async function getExistingSeqs(journalDir) {
|
|
|
31
32
|
}
|
|
32
33
|
async function appendEvent(opts) {
|
|
33
34
|
const journalDir = (0, paths_1.getJournalDir)(opts.runDir);
|
|
35
|
+
await (0, icloudWarning_1.warnIfICloudDrivePath)(journalDir);
|
|
34
36
|
await fs_1.promises.mkdir(journalDir, { recursive: true });
|
|
35
37
|
const seqs = await getExistingSeqs(journalDir);
|
|
36
38
|
const seq = (seqs.length ? Math.max(...seqs) : 0) + 1;
|
package/package.json
CHANGED