@alecsibilia/luca 14.0.0-alpha.2 → 14.0.0-alpha.21
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/CHANGELOG.md +76 -0
- package/README.md +7 -2
- package/board/client/board-look.ts +31 -1
- package/board/client/board-panel-parts.tsx +150 -0
- package/board/client/board-panel-stages.ts +99 -0
- package/board/client/board-panel-styles.ts +223 -0
- package/board/client/board-panel.tsx +77 -1275
- package/board/client/engine-life.ts +48 -0
- package/board/client/final-review-stack.tsx +144 -0
- package/board/client/heartbeat.tsx +61 -0
- package/board/client/helper-lines.tsx +69 -0
- package/board/client/live-feed.tsx +86 -0
- package/board/client/live-line.ts +73 -0
- package/board/client/model-line.tsx +178 -0
- package/board/client/needs-you-section.tsx +133 -0
- package/board/client/one-line.tsx +193 -0
- package/board/client/repo-wait-banner.tsx +69 -0
- package/board/client/run-footer.tsx +68 -0
- package/board/client/run-header.tsx +189 -0
- package/board/client/run-picker.tsx +98 -0
- package/board/client/status-bar.tsx +131 -0
- package/board/client/step-dots.tsx +127 -0
- package/board/client/stuck-actions.tsx +269 -0
- package/board/client/ticket-card-view.tsx +238 -0
- package/board/client/ticket-detail.tsx +87 -0
- package/board/client/ticket-stack.tsx +119 -0
- package/board/client/ticket-wait.ts +188 -0
- package/board/client/timeline-rows.tsx +41 -14
- package/board/client/usage-line.tsx +51 -0
- package/board/client/wait-line.tsx +39 -0
- package/board/index.client.tsx +1 -1
- package/board/index.server.ts +7 -1
- package/board/package.json +1 -1
- package/board/server/board-panel-state-helpers.ts +67 -0
- package/board/server/board-server-check.ts +294 -0
- package/board/server/board-server-engine.ts +129 -0
- package/board/server/board-server-events.ts +134 -0
- package/board/server/board-server-read.ts +190 -0
- package/board/server/board-server-resume.ts +164 -0
- package/board/server/board-server-start.ts +174 -0
- package/board/server/board-server-store.ts +281 -0
- package/board/server/board-server-types.ts +114 -0
- package/board/server/board-server-util.ts +4 -0
- package/board/server/board-server.ts +52 -943
- package/board/server/board-vocabulary-engine.ts +144 -0
- package/board/server/board-vocabulary-review.ts +115 -0
- package/board/server/board-vocabulary-run.ts +145 -0
- package/board/server/board-vocabulary-shapes.ts +153 -0
- package/board/server/board-vocabulary.ts +20 -375
- package/board/server/current-step-panel-helpers.ts +108 -0
- package/board/server/describe-record.ts +403 -0
- package/board/server/engine-launch.ts +20 -3
- package/board/server/engine-restart-helpers.ts +105 -0
- package/board/server/engine-watch.ts +45 -5
- package/board/server/journal-rebuild-helpers.ts +169 -0
- package/board/server/live-file.ts +130 -0
- package/board/server/luca-version.ts +1 -1
- package/board/server/make-rows.ts +14 -629
- package/board/server/record-texts.ts +350 -0
- package/board/server/reduce-board-agent-start.ts +224 -0
- package/board/server/reduce-board-agents.ts +160 -0
- package/board/server/reduce-board-counters.ts +138 -0
- package/board/server/reduce-board-create.ts +148 -0
- package/board/server/reduce-board-final-review.ts +174 -0
- package/board/server/reduce-board-needs-you.ts +283 -0
- package/board/server/reduce-board-reasons.ts +59 -0
- package/board/server/reduce-board-run.ts +255 -0
- package/board/server/reduce-board-settle.ts +88 -0
- package/board/server/reduce-board-steps.ts +226 -0
- package/board/server/reduce-board-text.ts +139 -0
- package/board/server/reduce-board-ticket-checks.ts +168 -0
- package/board/server/reduce-board-ticket-join.ts +144 -0
- package/board/server/reduce-board-ticket-start.ts +132 -0
- package/board/server/reduce-board-update.ts +117 -0
- package/board/server/reduce-board-usage.ts +247 -0
- package/board/server/reduce-board.ts +100 -1917
- package/board/server/reply-post.ts +407 -0
- package/board/server/run-registry.ts +8 -4
- package/board/server/ticket-stages-panel-helpers.ts +296 -0
- package/board/shared/board-rows.ts +9 -0
- package/board/shared/board-rpc.ts +42 -0
- package/board/shared/board-state-counts.ts +77 -0
- package/board/shared/board-state-live.ts +99 -0
- package/board/shared/board-state-review.ts +59 -0
- package/board/shared/board-state-run.ts +236 -0
- package/board/shared/board-state-ticket.ts +257 -0
- package/board/shared/board-state-usage.ts +107 -0
- package/board/shared/board-state.ts +97 -567
- package/board/shared/reply-actions.ts +207 -0
- package/engine/agents/agent-launcher.ts +27 -4
- package/engine/agents/claude-launcher-helpers.ts +233 -0
- package/engine/agents/claude-launcher.ts +28 -669
- package/engine/agents/claude-messages.ts +212 -0
- package/engine/agents/claude-options.ts +43 -8
- package/engine/agents/common-instructions.ts +138 -0
- package/engine/agents/input-channel.ts +36 -0
- package/engine/agents/launch-paths.ts +29 -0
- package/engine/agents/launch-session.ts +227 -0
- package/engine/agents/learner-instructions.ts +31 -0
- package/engine/agents/open-session.ts +210 -0
- package/engine/agents/prompt-sections.ts +95 -0
- package/engine/agents/rejoin-prompts.ts +135 -0
- package/engine/agents/reviewer-instructions.ts +99 -0
- package/engine/agents/role-instructions.ts +31 -244
- package/engine/agents/role-prompts.ts +39 -109
- package/engine/agents/role-results.ts +106 -7
- package/engine/agents/scripted-launcher.ts +21 -6
- package/engine/agents/session-pool.ts +124 -0
- package/engine/agents/turn-progress.ts +283 -0
- package/engine/agents/writer-instructions.ts +151 -0
- package/engine/cli/computer-adapters-real.ts +15 -3
- package/engine/cli/computer-adapters.ts +2 -0
- package/engine/cli/computer-checks.ts +87 -4
- package/engine/cli/config-reload-text.ts +39 -0
- package/engine/cli/decision-model-setup.ts +70 -0
- package/engine/cli/doctor-command.ts +1 -0
- package/engine/cli/doctor.ts +21 -3
- package/engine/cli/going-runs.ts +192 -36
- package/engine/cli/init-command.ts +1 -0
- package/engine/cli/init.ts +23 -1
- package/engine/cli/live-runs.ts +73 -0
- package/engine/cli/luca-run.ts +42 -7
- package/engine/cli/luca-skills.ts +183 -0
- package/engine/cli/luca.ts +8 -1
- package/engine/cli/models-command.ts +119 -0
- package/engine/cli/read-run.ts +81 -0
- package/engine/cli/run-args.ts +33 -6
- package/engine/cli/run-demo.ts +143 -0
- package/engine/cli/run-end.ts +184 -0
- package/engine/cli/run-modes-helpers.ts +81 -0
- package/engine/cli/run-modes.ts +62 -439
- package/engine/cli/setup.ts +5 -48
- package/engine/cli/unfinished-runs.ts +122 -0
- package/engine/cli/upgrade-command.ts +212 -30
- package/engine/cli/upgrade-finish.ts +149 -0
- package/engine/cli/upgrade.ts +112 -71
- package/engine/config/config-fields.ts +127 -0
- package/engine/config/config-reload.ts +250 -0
- package/engine/config/engine-config-schema.ts +235 -0
- package/engine/config/engine-config.ts +72 -142
- package/engine/config/test-commands.ts +102 -0
- package/engine/core/agent-guards-helpers.ts +84 -0
- package/engine/core/agent-turn.ts +395 -0
- package/engine/core/already-done.ts +44 -1
- package/engine/core/build-action.ts +214 -0
- package/engine/core/build-context.ts +135 -0
- package/engine/core/build-launch.ts +259 -0
- package/engine/core/commit-in.ts +282 -0
- package/engine/core/decide-build-early.ts +108 -0
- package/engine/core/decide-build-ending.ts +145 -0
- package/engine/core/decide-build-helpers.ts +87 -0
- package/engine/core/decide-build-tickets.ts +207 -0
- package/engine/core/decide-build-view.ts +103 -0
- package/engine/core/decide-build.ts +101 -1368
- package/engine/core/decide-final-fix.ts +203 -0
- package/engine/core/decide-final-review.ts +55 -310
- package/engine/core/decide-red-green.ts +398 -0
- package/engine/core/decide-rejoin.ts +259 -0
- package/engine/core/decide-repo-wait.ts +129 -0
- package/engine/core/decide-run-budget.ts +86 -12
- package/engine/core/decide-stuck-helpers.ts +127 -0
- package/engine/core/decide-stuck.ts +60 -8
- package/engine/core/decide-ticket-review.ts +95 -0
- package/engine/core/decide-ticket.ts +267 -0
- package/engine/core/decide-waits.ts +47 -0
- package/engine/core/decide.ts +74 -23
- package/engine/core/engine-chores.ts +163 -0
- package/engine/core/environment-rerun.ts +83 -0
- package/engine/core/evidence-text.ts +84 -0
- package/engine/core/execute-action.ts +303 -0
- package/engine/core/execute-already-done-check.ts +133 -0
- package/engine/core/execute-build.ts +68 -944
- package/engine/core/execute-changeset.ts +5 -7
- package/engine/core/execute-final-review.ts +125 -13
- package/engine/core/execute-gates.ts +182 -0
- package/engine/core/execute-intake.ts +130 -0
- package/engine/core/execute-join.ts +276 -0
- package/engine/core/execute-memory.ts +12 -0
- package/engine/core/execute-pull-request.ts +84 -0
- package/engine/core/execute-red-check.ts +219 -0
- package/engine/core/execute-repo-wait.ts +129 -0
- package/engine/core/execute-stuck.ts +25 -10
- package/engine/core/execute-waits.ts +158 -0
- package/engine/core/execute-with-jev.ts +118 -0
- package/engine/core/execute.ts +124 -831
- package/engine/core/final-failure-cap.ts +38 -0
- package/engine/core/final-review-action.ts +125 -0
- package/engine/core/final-review-text.ts +63 -17
- package/engine/core/final-stuck-text.ts +86 -0
- package/engine/core/fix-loop-text.ts +49 -2
- package/engine/core/left-out.ts +120 -0
- package/engine/core/live-file.ts +372 -0
- package/engine/core/loop-caps.ts +25 -2
- package/engine/core/not-in-run.ts +7 -3
- package/engine/core/pull-request-text.ts +255 -6
- package/engine/core/repo-wait-text.ts +81 -0
- package/engine/core/review-text.ts +55 -1
- package/engine/core/run-crash-recovery-helpers.ts +16 -0
- package/engine/core/run-session-close-helpers.ts +96 -0
- package/engine/core/run-start.ts +148 -0
- package/engine/core/run-stuck-text.ts +86 -0
- package/engine/core/schedule-rules.ts +193 -0
- package/engine/core/stuck-detail-text.ts +89 -0
- package/engine/core/stuck-format.ts +22 -0
- package/engine/core/stuck-text.ts +29 -332
- package/engine/core/ticket-stuck-text.ts +192 -0
- package/engine/core/worktree-changes.ts +106 -0
- package/engine/gates/already-done-check.ts +120 -0
- package/engine/gates/feature-map.ts +96 -0
- package/engine/gates/gate-runner.ts +108 -108
- package/engine/gates/gate-schemas.ts +61 -2
- package/engine/gates/leftover-scan.ts +154 -31
- package/engine/gates/new-tests.ts +45 -0
- package/engine/gates/own-files-check.ts +98 -0
- package/engine/gates/prepare-check.ts +60 -0
- package/engine/gates/prepare-slots.ts +99 -0
- package/engine/gates/red-check.ts +184 -16
- package/engine/gates/shell-check.ts +64 -0
- package/engine/gates/slot-waits.ts +55 -0
- package/engine/gates/slow-test-runner.ts +59 -0
- package/engine/gates/test-checks.ts +161 -0
- package/engine/gates/test-runner.ts +164 -5
- package/engine/gates/test-slots.ts +48 -0
- package/engine/git/git-adapter.ts +55 -396
- package/engine/git/git-commits.ts +132 -0
- package/engine/git/git-join.ts +139 -0
- package/engine/git/git-read.ts +123 -0
- package/engine/git/git-run.ts +32 -0
- package/engine/git/git-worktrees.ts +143 -0
- package/engine/git/outside-links.ts +55 -0
- package/engine/guards/after-turn-check.ts +31 -5
- package/engine/guards/guard-hook.ts +60 -12
- package/engine/guards/guard-roles.ts +68 -0
- package/engine/guards/helper-rules.ts +92 -0
- package/engine/guards/permission-rules.ts +161 -0
- package/engine/guards/role-rules-helpers.ts +48 -0
- package/engine/guards/role-rules.ts +34 -606
- package/engine/guards/shell-rules.ts +344 -0
- package/engine/guards/worktree-git.ts +208 -0
- package/engine/guards/worktree-snapshot.ts +412 -0
- package/engine/guards/worktree-state.ts +28 -825
- package/engine/guards/worktree-undo.ts +260 -0
- package/engine/guards/write-rules.ts +149 -0
- package/engine/index.ts +30 -9
- package/engine/intake/criteria-checks.ts +114 -0
- package/engine/intake/intake-checks.ts +164 -30
- package/engine/intake/intake-schemas.ts +37 -2
- package/engine/jev/clef-client.ts +259 -0
- package/engine/jev/decision-model-credentials.ts +212 -0
- package/engine/jev/jev-jobs.ts +144 -60
- package/engine/jev/jev-schemas.ts +86 -5
- package/engine/jev/jev-shadow-helpers.ts +218 -0
- package/engine/jev/jev-shadow.ts +109 -64
- package/engine/jev/model-pick.ts +275 -0
- package/engine/journal/agent-entries.ts +167 -0
- package/engine/journal/agent-message-entries.ts +72 -0
- package/engine/journal/agent-turn-replay.ts +203 -0
- package/engine/journal/already-done-entries.ts +55 -0
- package/engine/journal/commit-entries.ts +56 -0
- package/engine/journal/cross-ticket-replay.ts +222 -0
- package/engine/journal/decision-model-entries.ts +86 -0
- package/engine/journal/entry-fields.ts +10 -0
- package/engine/journal/final-review-entries.ts +128 -0
- package/engine/journal/final-review-replay.ts +324 -0
- package/engine/journal/final-review-round.ts +320 -0
- package/engine/journal/final-review-state.ts +180 -0
- package/engine/journal/gate-entries.ts +81 -0
- package/engine/journal/intake-entries.ts +87 -0
- package/engine/journal/join-entries.ts +126 -0
- package/engine/journal/journal-entry-schema.ts +218 -0
- package/engine/journal/journal-kinds.ts +97 -0
- package/engine/journal/journal-record-schema.ts +218 -0
- package/engine/journal/journal-record.ts +43 -1400
- package/engine/journal/journal.ts +6 -1
- package/engine/journal/limit-entries.ts +90 -0
- package/engine/journal/memory-entries.ts +124 -0
- package/engine/journal/memory-replay.ts +157 -0
- package/engine/journal/plan-replay.ts +139 -0
- package/engine/journal/pull-request-entries.ts +41 -0
- package/engine/journal/replay.ts +157 -2144
- package/engine/journal/reply-replay.ts +144 -0
- package/engine/journal/repo-wait-entries.ts +53 -0
- package/engine/journal/run-entries.ts +172 -0
- package/engine/journal/run-state.ts +264 -0
- package/engine/journal/shared-failure-replay.ts +133 -0
- package/engine/journal/stuck-entries.ts +193 -0
- package/engine/journal/ticket-progress.ts +392 -0
- package/engine/journal/ticket-replay.ts +411 -0
- package/engine/journal/ticket-result-replay.ts +364 -0
- package/engine/journal/ticket-retry-replay.ts +142 -0
- package/engine/journal/worktree-entries.ts +122 -0
- package/engine/models/model-score.ts +512 -0
- package/engine/shell/run-command.ts +187 -18
- package/engine/testing/agent-turn-fixtures.ts +287 -0
- package/engine/testing/already-done-fixtures.ts +97 -0
- package/engine/testing/build-fixtures.ts +68 -973
- package/engine/testing/checkouts-fixtures.ts +48 -0
- package/engine/testing/final-review-fixtures.ts +62 -1
- package/engine/testing/final-review-steps.ts +143 -0
- package/engine/testing/limit-fixtures.ts +85 -0
- package/engine/testing/live-file-fixtures.ts +57 -0
- package/engine/testing/practice-repo.ts +10 -4
- package/engine/testing/practice-run.ts +1 -1
- package/engine/testing/reply-fixtures.ts +88 -0
- package/engine/testing/run-budget-fixtures.ts +165 -0
- package/engine/testing/run-fixtures.ts +219 -0
- package/engine/testing/sdk-play.ts +177 -0
- package/engine/testing/setup-repo.ts +194 -0
- package/engine/testing/step-record-fixtures.ts +55 -0
- package/engine/testing/ticket-step-fixtures.ts +272 -0
- package/engine/testing/wait-scenarios.ts +186 -0
- package/engine/tracker/gh-retry.ts +217 -0
- package/engine/tracker/github-tracker.ts +310 -90
- package/engine/tracker/in-memory-tracker.ts +8 -4
- package/engine/tracker/tracker.ts +131 -6
- package/package.json +2 -1
- package/skills/luca-retro/SKILL.md +74 -0
- package/skills/luca-retro/scripts/retro-summary.ts +1356 -0
- package/skills/luca-unstick/SKILL.md +153 -0
- package/skills/luca-unstick/scripts/stuck-summary.ts +1063 -0
- package/engine/jev/jev-client.ts +0 -173
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The practice run, `luca-run --demo` (`runDemo`): a throwaway repo, scripted
|
|
3
|
+
* agents, and a fake MuninnDB, safe to try the board with.
|
|
4
|
+
*/
|
|
5
|
+
import { mkdtemp, rm } from 'node:fs/promises'
|
|
6
|
+
import { tmpdir } from 'node:os'
|
|
7
|
+
import { join } from 'node:path'
|
|
8
|
+
|
|
9
|
+
import { errorText, driveRun, type RunEnd } from './run-end'
|
|
10
|
+
|
|
11
|
+
import type { AgentLauncher } from '../agents/agent-launcher'
|
|
12
|
+
import { createScriptedLauncher } from '../agents/scripted-launcher'
|
|
13
|
+
import type { BoardSync } from '../board/board-sync'
|
|
14
|
+
import { loadEngineConfig } from '../config/engine-config'
|
|
15
|
+
import { runEngine, startRun } from '../core/execute'
|
|
16
|
+
import { createGitAdapter } from '../git/git-adapter'
|
|
17
|
+
import { createJournal, runJournalPath } from '../journal/journal'
|
|
18
|
+
import {
|
|
19
|
+
DEMO_TURNS,
|
|
20
|
+
demoMuninn,
|
|
21
|
+
demoTracker,
|
|
22
|
+
makePracticeRepo,
|
|
23
|
+
PRACTICE_SPEC_NUMBER,
|
|
24
|
+
} from '../testing/practice-run'
|
|
25
|
+
import type { InMemoryPullRequest } from '../tracker/in-memory-tracker'
|
|
26
|
+
|
|
27
|
+
/** How long each scripted agent turn of the demo takes, so the board is watchable. */
|
|
28
|
+
export const DEMO_TURN_DELAY_MS = 1500
|
|
29
|
+
|
|
30
|
+
/** Wraps a launcher so each agent turn takes at least `delay_ms`. */
|
|
31
|
+
const slowLauncher = ({
|
|
32
|
+
launcher,
|
|
33
|
+
delay_ms,
|
|
34
|
+
log,
|
|
35
|
+
}: {
|
|
36
|
+
launcher: AgentLauncher
|
|
37
|
+
delay_ms: number
|
|
38
|
+
log: (line: string) => void
|
|
39
|
+
}): AgentLauncher => ({
|
|
40
|
+
launch: async (args) => {
|
|
41
|
+
log(`[luca-run] ${args.role} on #${args.ticket}`)
|
|
42
|
+
await Bun.sleep(delay_ms)
|
|
43
|
+
return launcher.launch(args)
|
|
44
|
+
},
|
|
45
|
+
followUp: async (args) => {
|
|
46
|
+
log(`[luca-run] ${args.role} on #${args.ticket} (follow-up)`)
|
|
47
|
+
await Bun.sleep(delay_ms)
|
|
48
|
+
return launcher.followUp(args)
|
|
49
|
+
},
|
|
50
|
+
closeSession: (args) => launcher.closeSession(args),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A practice run, safe to try the board with: a throwaway repo with a local
|
|
55
|
+
* bare origin in a temp folder, the practice spec with two tickets (#12
|
|
56
|
+
* blocked by #11) in an in-memory tracker, and scripted agents that take
|
|
57
|
+
* `turn_delay_ms` per turn. No GitHub, no models, no network: the decision
|
|
58
|
+
* model is off (its credentials are never read), and memory (#370) is a fake MuninnDB seeded with
|
|
59
|
+
* a few memories, so the board shows searches, the learner, and a save
|
|
60
|
+
* with nothing leaving the machine. The temp folder (which also holds the run's
|
|
61
|
+
* journal) is removed at the end.
|
|
62
|
+
*
|
|
63
|
+
* @returns The run's end, the PRs the in-memory tracker opened, and the
|
|
64
|
+
* (removed) temp folder.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* const { ok, pull_requests } = await runDemo({ run_id, board: null, log: console.log, turn_delay_ms: DEMO_TURN_DELAY_MS })
|
|
68
|
+
*/
|
|
69
|
+
export const runDemo = async ({
|
|
70
|
+
run_id,
|
|
71
|
+
board,
|
|
72
|
+
log,
|
|
73
|
+
turn_delay_ms,
|
|
74
|
+
}: {
|
|
75
|
+
run_id: string
|
|
76
|
+
board: BoardSync | null
|
|
77
|
+
log: (line: string) => void
|
|
78
|
+
turn_delay_ms: number
|
|
79
|
+
}): Promise<
|
|
80
|
+
RunEnd & { pull_requests: InMemoryPullRequest[]; root: string }
|
|
81
|
+
> => {
|
|
82
|
+
const root = await mkdtemp(join(tmpdir(), 'luca-demo-'))
|
|
83
|
+
try {
|
|
84
|
+
const { repo, origin } = await makePracticeRepo({ root })
|
|
85
|
+
const journal = createJournal({
|
|
86
|
+
file: runJournalPath({ runs_dir: join(root, 'runs'), run_id }),
|
|
87
|
+
})
|
|
88
|
+
log(`[luca-run] demo run ${run_id} in ${root}`)
|
|
89
|
+
log(`[luca-run] repo: ${repo}`)
|
|
90
|
+
log(`[luca-run] origin: ${origin}`)
|
|
91
|
+
log(`[luca-run] journal: ${journal.file}`)
|
|
92
|
+
log('[luca-run] decision model off: the demo never asks it.')
|
|
93
|
+
const loaded = await loadEngineConfig({ repo_root: repo })
|
|
94
|
+
if (!loaded.ok) throw new Error(loaded.error)
|
|
95
|
+
const tracker = demoTracker()
|
|
96
|
+
startRun({
|
|
97
|
+
journal,
|
|
98
|
+
spec_number: PRACTICE_SPEC_NUMBER,
|
|
99
|
+
config: loaded.config,
|
|
100
|
+
base_branch: 'main',
|
|
101
|
+
memory: { project_vault: null },
|
|
102
|
+
repo,
|
|
103
|
+
})
|
|
104
|
+
const memory = { client: demoMuninn() }
|
|
105
|
+
const launcher = slowLauncher({
|
|
106
|
+
launcher: createScriptedLauncher({
|
|
107
|
+
turns: DEMO_TURNS,
|
|
108
|
+
}),
|
|
109
|
+
delay_ms: turn_delay_ms,
|
|
110
|
+
log,
|
|
111
|
+
})
|
|
112
|
+
const end = await driveRun({
|
|
113
|
+
journal,
|
|
114
|
+
// A demo's journal is in a temp folder that is removed at its end.
|
|
115
|
+
run_id: null,
|
|
116
|
+
launcher,
|
|
117
|
+
memory,
|
|
118
|
+
board,
|
|
119
|
+
log,
|
|
120
|
+
run: () =>
|
|
121
|
+
runEngine({
|
|
122
|
+
journal,
|
|
123
|
+
tracker,
|
|
124
|
+
git: createGitAdapter({ repo_root: repo }),
|
|
125
|
+
launcher,
|
|
126
|
+
memory,
|
|
127
|
+
board: board ?? undefined,
|
|
128
|
+
}),
|
|
129
|
+
})
|
|
130
|
+
return { ...end, pull_requests: tracker.pullRequests(), root }
|
|
131
|
+
} catch (error) {
|
|
132
|
+
const end = {
|
|
133
|
+
ok: false,
|
|
134
|
+
message: `The demo crashed: ${errorText(error)}`,
|
|
135
|
+
}
|
|
136
|
+
log(`[luca-run] stopped: ${end.message}`)
|
|
137
|
+
await board?.end(end)
|
|
138
|
+
return { ...end, pull_requests: [], root }
|
|
139
|
+
} finally {
|
|
140
|
+
await rm(root, { recursive: true, force: true })
|
|
141
|
+
log(`[luca-run] removed ${root}`)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a run ends for the person watching it: what a run's last action means
|
|
3
|
+
* (`RunEnd`), how to go on from its journal (`resumeCommand`), and driving the
|
|
4
|
+
* engine to its end without ever throwing (`driveRun`).
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentLauncher } from '../agents/agent-launcher'
|
|
7
|
+
import type { BoardSync } from '../board/board-sync'
|
|
8
|
+
import type { EngineAction } from '../core/decide'
|
|
9
|
+
import { nothingToDoText } from '../core/left-out'
|
|
10
|
+
import type { Journal } from '../journal/journal'
|
|
11
|
+
import type { JournalRecord } from '../journal/journal-record'
|
|
12
|
+
import type { MemoryDeps } from '../memory/memory-client'
|
|
13
|
+
|
|
14
|
+
/** How a `luca-run` process ended; the board gets it as `ended`. */
|
|
15
|
+
export type RunEnd = { ok: boolean; message: string }
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* An agent launcher a run may close at its end, such as the Claude launcher,
|
|
19
|
+
* which keeps sessions open for follow-ups.
|
|
20
|
+
*/
|
|
21
|
+
export type RunLauncher = AgentLauncher & { closeAll?: () => Promise<void> }
|
|
22
|
+
|
|
23
|
+
/** An error's message, or any other thrown value as text. */
|
|
24
|
+
export const errorText = (error: unknown): string =>
|
|
25
|
+
error instanceof Error ? error.message : String(error)
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* What a run's last action means for the person watching it. A run with
|
|
29
|
+
* nothing to do says why, from its `nothing_to_do` record in `records`.
|
|
30
|
+
*/
|
|
31
|
+
const endOf = ({
|
|
32
|
+
action,
|
|
33
|
+
records,
|
|
34
|
+
}: {
|
|
35
|
+
action: EngineAction
|
|
36
|
+
records: JournalRecord[]
|
|
37
|
+
}): RunEnd => {
|
|
38
|
+
if (action.type === 'invalid_journal') {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
message: `The journal is not a run: ${action.reason}`,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (action.type !== 'done') {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
message: `The engine stopped before ${action.type}.`,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
switch (action.outcome) {
|
|
51
|
+
case 'pr_opened':
|
|
52
|
+
return {
|
|
53
|
+
ok: true,
|
|
54
|
+
message: `PR opened: ${action.pull_request.url}`,
|
|
55
|
+
}
|
|
56
|
+
case 'nothing_to_do': {
|
|
57
|
+
const end = records.findLast(
|
|
58
|
+
(record) => record.kind === 'nothing_to_do'
|
|
59
|
+
)
|
|
60
|
+
return {
|
|
61
|
+
ok: true,
|
|
62
|
+
message: nothingToDoText({
|
|
63
|
+
content:
|
|
64
|
+
end?.kind === 'nothing_to_do'
|
|
65
|
+
? end.content
|
|
66
|
+
: {
|
|
67
|
+
closed_tickets: [],
|
|
68
|
+
already_done: [],
|
|
69
|
+
left_out: [],
|
|
70
|
+
},
|
|
71
|
+
}),
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
case 'refused':
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
message:
|
|
78
|
+
'Intake refused the run; the spec and tickets have comments saying what is missing.',
|
|
79
|
+
}
|
|
80
|
+
case 'stopped_by_user':
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
message:
|
|
84
|
+
"Stopped by your `stop` reply: no PR. The run branch and the unfinished tickets' worktrees are kept.",
|
|
85
|
+
}
|
|
86
|
+
case 'all_skipped':
|
|
87
|
+
return {
|
|
88
|
+
ok: true,
|
|
89
|
+
message:
|
|
90
|
+
'Every ticket was skipped, so there is no PR. The skipped tickets stay open.',
|
|
91
|
+
}
|
|
92
|
+
case 'stopped':
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
message: `Run stopped for billing: ${action.reason}. It will not go on; start a new run once per-token billing is off.`,
|
|
96
|
+
}
|
|
97
|
+
case 'crashed':
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
message: `Run stopped for good after crashes: ${action.reason} It will not go on; read the engine's log for why it crashed, then start a new run.`,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* How to go on with run `run_id` from its journal: `/luca-run resume <id>`
|
|
107
|
+
* in Paseo when the board plugin started it (so the board shows it live),
|
|
108
|
+
* else `luca-run --resume <id>`. Pure.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* resumeCommand({ run_id: 'r1', board: true }) // 'type /luca-run resume r1 in a Paseo chat'
|
|
112
|
+
* resumeCommand({ run_id: 'r1', board: false }) // 'run luca-run --resume r1'
|
|
113
|
+
*/
|
|
114
|
+
export const resumeCommand = ({
|
|
115
|
+
run_id,
|
|
116
|
+
board,
|
|
117
|
+
}: {
|
|
118
|
+
run_id: string
|
|
119
|
+
board: boolean
|
|
120
|
+
}): string =>
|
|
121
|
+
board
|
|
122
|
+
? `type /luca-run resume ${run_id} in a Paseo chat`
|
|
123
|
+
: `run luca-run --resume ${run_id}`
|
|
124
|
+
|
|
125
|
+
/** `text` as a sentence, then `more`. Pure. */
|
|
126
|
+
const thenSay = ({ text, more }: { text: string; more: string }): string =>
|
|
127
|
+
`${text}${/[.!?]$/.test(text) ? '' : '.'} ${more}`
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Runs the engine to its end and turns what happened into a `RunEnd`, then
|
|
131
|
+
* closes the launcher's open sessions and tells the board. A crash is caught
|
|
132
|
+
* and reported, never thrown. With `run_id`, a crash or a launcher stop says
|
|
133
|
+
* how to go on from the journal (see `resumeCommand`).
|
|
134
|
+
*/
|
|
135
|
+
export const driveRun = async ({
|
|
136
|
+
journal,
|
|
137
|
+
run,
|
|
138
|
+
run_id,
|
|
139
|
+
launcher,
|
|
140
|
+
memory,
|
|
141
|
+
board,
|
|
142
|
+
log,
|
|
143
|
+
}: {
|
|
144
|
+
journal: Journal
|
|
145
|
+
run: () => Promise<EngineAction>
|
|
146
|
+
/** The run's id, or `null` for one that can't go on again (the demo). */
|
|
147
|
+
run_id: string | null
|
|
148
|
+
launcher: RunLauncher
|
|
149
|
+
/** Closed at the end too. */
|
|
150
|
+
memory?: MemoryDeps
|
|
151
|
+
board: BoardSync | null
|
|
152
|
+
log: (line: string) => void
|
|
153
|
+
}): Promise<RunEnd> => {
|
|
154
|
+
let end: RunEnd
|
|
155
|
+
try {
|
|
156
|
+
const action = await run()
|
|
157
|
+
end = endOf({ action, records: journal.read() })
|
|
158
|
+
} catch (error) {
|
|
159
|
+
const message = errorText(error)
|
|
160
|
+
// A launcher stop is journaled as run_stopped, then thrown.
|
|
161
|
+
const stopped = message.startsWith('Run stopped:')
|
|
162
|
+
const text = stopped ? message : `The engine crashed: ${message}`
|
|
163
|
+
if (run_id === null) {
|
|
164
|
+
end = { ok: false, message: text }
|
|
165
|
+
} else {
|
|
166
|
+
const how = resumeCommand({ run_id, board: board !== null })
|
|
167
|
+
end = {
|
|
168
|
+
ok: false,
|
|
169
|
+
message: thenSay({
|
|
170
|
+
text,
|
|
171
|
+
more: stopped
|
|
172
|
+
? `Fix that, then, to go on from its journal, ${how}.`
|
|
173
|
+
: `To go on from its journal, ${how}.`,
|
|
174
|
+
}),
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} finally {
|
|
178
|
+
await launcher.closeAll?.()
|
|
179
|
+
await memory?.client.close().catch(() => undefined)
|
|
180
|
+
}
|
|
181
|
+
log(`[luca-run] ${end.ok ? 'finished' : 'stopped'}: ${end.message}`)
|
|
182
|
+
await board?.end({ records: journal.read(), ...end })
|
|
183
|
+
return end
|
|
184
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { RunLauncher } from './run-modes'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createScriptedLauncher,
|
|
5
|
+
type ScriptedTurn,
|
|
6
|
+
} from '../agents/scripted-launcher'
|
|
7
|
+
import {
|
|
8
|
+
createBoardSync,
|
|
9
|
+
type BoardEnded,
|
|
10
|
+
type BoardLink,
|
|
11
|
+
} from '../board/board-sync'
|
|
12
|
+
import { createJournal, runJournalPath } from '../journal/journal'
|
|
13
|
+
import type { JournalRecord } from '../journal/journal-record'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* What the run modes' end-to-end tests share: a stand-in for the Claude
|
|
17
|
+
* launcher, a board that keeps what it is sent, and readers of a run's journal.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A stand-in for the Claude launcher: scripted turns, plus a `closeAll`
|
|
22
|
+
* that counts its calls.
|
|
23
|
+
*/
|
|
24
|
+
export const fakeClaudeLauncher = ({ turns }: { turns: ScriptedTurn[] }) => {
|
|
25
|
+
const scripted = createScriptedLauncher({ turns })
|
|
26
|
+
let closed = 0
|
|
27
|
+
const launcher: RunLauncher = {
|
|
28
|
+
launch: scripted.launch,
|
|
29
|
+
followUp: scripted.followUp,
|
|
30
|
+
closeSession: scripted.closeSession,
|
|
31
|
+
closeAll: async () => {
|
|
32
|
+
closed += 1
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
return { launcher, closed: () => closed, launches: scripted.launches }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A board that keeps everything it is sent. */
|
|
39
|
+
export const recordingBoard = () => {
|
|
40
|
+
const kinds: string[] = []
|
|
41
|
+
const seqs: number[] = []
|
|
42
|
+
const endings: BoardEnded[] = []
|
|
43
|
+
const link: BoardLink = {
|
|
44
|
+
send: async ({ records, ended }) => {
|
|
45
|
+
for (const record of records) {
|
|
46
|
+
if (record.seq === seqs.length + 1) {
|
|
47
|
+
seqs.push(record.seq)
|
|
48
|
+
kinds.push(record.kind)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (ended !== null) endings.push(ended)
|
|
52
|
+
return { ok: true, next_seq: seqs.length + 1, message: 'ok' }
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
board: createBoardSync({ link }),
|
|
57
|
+
/** The kinds it got, without the scheduler's step records. */
|
|
58
|
+
kinds: () =>
|
|
59
|
+
kinds.filter(
|
|
60
|
+
(kind) =>
|
|
61
|
+
kind !== 'step_started' &&
|
|
62
|
+
kind !== 'step_ended' &&
|
|
63
|
+
kind !== 'run_resumed'
|
|
64
|
+
),
|
|
65
|
+
endings: () => endings,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The records of run `run_id` in `runs_dir`. */
|
|
70
|
+
export const recordsOf = ({
|
|
71
|
+
runs_dir,
|
|
72
|
+
run_id,
|
|
73
|
+
}: {
|
|
74
|
+
runs_dir: string
|
|
75
|
+
run_id: string
|
|
76
|
+
}): JournalRecord[] =>
|
|
77
|
+
createJournal({ file: runJournalPath({ runs_dir, run_id }) }).read()
|
|
78
|
+
|
|
79
|
+
/** The kinds of a run's records, oldest first. */
|
|
80
|
+
export const kindsIn = (records: JournalRecord[]) =>
|
|
81
|
+
records.map(({ kind }) => kind)
|