@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,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guard's roles: which role each agent plays by, the tools each sees, and
|
|
3
|
+
* the shape of a guard decision.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod'
|
|
6
|
+
|
|
7
|
+
import type { AgentRole } from '../agents/role-results'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The roles the **guard** knows. `reviewer` covers every reviewer (the ticket
|
|
11
|
+
* reviewer and the final review's lenses). The learner has no agent
|
|
12
|
+
* role yet (#370) but its rules are here already.
|
|
13
|
+
*/
|
|
14
|
+
export const GuardRoleSchema = z.enum([
|
|
15
|
+
'test-writer',
|
|
16
|
+
'implementer',
|
|
17
|
+
'reviewer',
|
|
18
|
+
'learner',
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
export type GuardRole = z.infer<typeof GuardRoleSchema>
|
|
22
|
+
|
|
23
|
+
const GUARD_ROLE_OF: Record<AgentRole, GuardRole> = {
|
|
24
|
+
'test-writer': 'test-writer',
|
|
25
|
+
implementer: 'implementer',
|
|
26
|
+
'ticket-reviewer': 'reviewer',
|
|
27
|
+
'architecture-lens': 'reviewer',
|
|
28
|
+
'simplification-lens': 'reviewer',
|
|
29
|
+
'security-lens': 'reviewer',
|
|
30
|
+
'integration-lens': 'reviewer',
|
|
31
|
+
'rules-lens': 'reviewer',
|
|
32
|
+
learner: 'learner',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The guard role an agent role plays by. */
|
|
36
|
+
export const guardRoleOf = ({ role }: { role: AgentRole }): GuardRole =>
|
|
37
|
+
GUARD_ROLE_OF[role]
|
|
38
|
+
|
|
39
|
+
/** Whether a guard role may change files at all. */
|
|
40
|
+
export const isWriter = (role: GuardRole): boolean =>
|
|
41
|
+
role === 'test-writer' || role === 'implementer'
|
|
42
|
+
|
|
43
|
+
/** What the guard says about one tool call. */
|
|
44
|
+
export type ToolDecision = { allow: true } | { allow: false; reason: string }
|
|
45
|
+
|
|
46
|
+
export const ALLOW: ToolDecision = { allow: true }
|
|
47
|
+
|
|
48
|
+
export const deny = (reason: string): ToolDecision => ({ allow: false, reason })
|
|
49
|
+
|
|
50
|
+
/** The rules an agent's guards check it against: its role, and whether it may edit tests. */
|
|
51
|
+
export type GuardArgs = {
|
|
52
|
+
role: GuardRole
|
|
53
|
+
/**
|
|
54
|
+
* Whether this agent may edit test files: true for the test-writer and a
|
|
55
|
+
* refactor ticket's implementer. A test-writer without it writes nothing;
|
|
56
|
+
* a reviewer or the learner never writes, whatever it says.
|
|
57
|
+
*/
|
|
58
|
+
may_edit_tests: boolean
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const READ_TOOLS = ['Read', 'Grep', 'Glob']
|
|
62
|
+
|
|
63
|
+
export const TOOLS: Record<GuardRole, string[]> = {
|
|
64
|
+
'test-writer': ['Read', 'Grep', 'Glob', 'Edit', 'Write', 'Bash'],
|
|
65
|
+
implementer: ['Read', 'Grep', 'Glob', 'Edit', 'Write', 'Bash'],
|
|
66
|
+
reviewer: ['Read', 'Grep', 'Glob', 'Bash'],
|
|
67
|
+
learner: ['Read', 'Grep', 'Glob'],
|
|
68
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure: the rules for **helpers** (#627), the read-only Haiku sessions an
|
|
3
|
+
* agent may start with the SDK's `Agent` tool to search the repo.
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod'
|
|
6
|
+
|
|
7
|
+
import { ALLOW, deny, READ_TOOLS, type ToolDecision } from './guard-roles'
|
|
8
|
+
|
|
9
|
+
/** The SDK tool that starts a helper. */
|
|
10
|
+
export const HELPER_TOOL = 'Agent'
|
|
11
|
+
|
|
12
|
+
/** The one helper type an agent may start. */
|
|
13
|
+
export const HELPER_TYPE = 'searcher'
|
|
14
|
+
|
|
15
|
+
/** Helpers always run on the newest Haiku, by its short name. */
|
|
16
|
+
export const HELPER_MODEL = 'haiku'
|
|
17
|
+
|
|
18
|
+
/** The only tools a helper has: it reads and searches, nothing else. */
|
|
19
|
+
export const HELPER_TOOLS = READ_TOOLS
|
|
20
|
+
|
|
21
|
+
/** How many helpers one agent may have running at once. */
|
|
22
|
+
export const MAX_HELPERS = 3
|
|
23
|
+
|
|
24
|
+
/** The `searcher` helper's definition, for the SDK's `agents` option. */
|
|
25
|
+
export const SEARCHER = {
|
|
26
|
+
description:
|
|
27
|
+
'A read-only helper that searches the repo: finds files, symbols and callers, and reports what it found. It cannot edit files or run commands.',
|
|
28
|
+
model: HELPER_MODEL,
|
|
29
|
+
tools: [...HELPER_TOOLS],
|
|
30
|
+
prompt: 'You are a read-only searcher. You may only read and search files in the current worktree with Read, Grep and Glob. Never try to change a file or run a command. Answer with what you found: file paths, line numbers and short excerpts, as briefly as the question allows.',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const HelperInputSchema = z.object({
|
|
34
|
+
subagent_type: z.string().optional(),
|
|
35
|
+
model: z.string().optional(),
|
|
36
|
+
run_in_background: z.boolean().optional(),
|
|
37
|
+
isolation: z.string().optional(),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The guard's decision for an `Agent` call that starts a helper, apart from
|
|
42
|
+
* how many already run. Only a `searcher`, on Haiku (never a Fable model),
|
|
43
|
+
* in the foreground, so it ends inside the agent's turn and the after-turn
|
|
44
|
+
* check sees what it did, and in the agent's own worktree.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* checkHelperCall({ tool_input: { subagent_type: 'searcher', run_in_background: false } }) // { allow: true }
|
|
48
|
+
*/
|
|
49
|
+
export const checkHelperCall = ({
|
|
50
|
+
tool_input,
|
|
51
|
+
}: {
|
|
52
|
+
tool_input: unknown
|
|
53
|
+
}): ToolDecision => {
|
|
54
|
+
const input = HelperInputSchema.safeParse(tool_input)
|
|
55
|
+
if (!input.success) return deny('Agent got an input of the wrong shape.')
|
|
56
|
+
const { subagent_type, model, run_in_background, isolation } = input.data
|
|
57
|
+
if (model !== undefined && /fable/i.test(model)) {
|
|
58
|
+
return deny(`Fable models are refused: ${model}`)
|
|
59
|
+
}
|
|
60
|
+
if (subagent_type !== HELPER_TYPE) {
|
|
61
|
+
return deny(
|
|
62
|
+
`The only helper is "${HELPER_TYPE}": ${subagent_type ?? 'no subagent_type'} is not allowed.`
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
if (model !== undefined && model !== HELPER_MODEL) {
|
|
66
|
+
return deny(`Helpers always run on ${HELPER_MODEL}: ${model}`)
|
|
67
|
+
}
|
|
68
|
+
if (run_in_background !== false) {
|
|
69
|
+
return deny(
|
|
70
|
+
'Helpers run in the foreground: set run_in_background to false.'
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
if (isolation !== undefined) {
|
|
74
|
+
return deny('Helpers work in your own worktree: no isolation.')
|
|
75
|
+
}
|
|
76
|
+
return ALLOW
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The guard's decision for a tool call a helper makes itself: only its
|
|
81
|
+
* read-only tools, whatever its agent may do.
|
|
82
|
+
*/
|
|
83
|
+
export const checkHelperTool = ({
|
|
84
|
+
tool_name,
|
|
85
|
+
}: {
|
|
86
|
+
tool_name: string
|
|
87
|
+
}): ToolDecision =>
|
|
88
|
+
HELPER_TOOLS.includes(tool_name)
|
|
89
|
+
? ALLOW
|
|
90
|
+
: deny(
|
|
91
|
+
`A helper may only use ${HELPER_TOOLS.join(', ')}: not ${tool_name}.`
|
|
92
|
+
)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure: each role's SDK permission settings, the layer under the guard hook.
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
READ_TOOLS,
|
|
6
|
+
TOOLS,
|
|
7
|
+
type GuardArgs,
|
|
8
|
+
type GuardRole,
|
|
9
|
+
} from './guard-roles'
|
|
10
|
+
import { HELPER_TOOL, HELPER_TYPE } from './helper-rules'
|
|
11
|
+
import {
|
|
12
|
+
checkCommands,
|
|
13
|
+
GIT_READ_SUBCOMMANDS,
|
|
14
|
+
PACKAGE_MANAGERS,
|
|
15
|
+
wordsOf,
|
|
16
|
+
} from './shell-rules'
|
|
17
|
+
import { LOCKFILES } from './write-rules'
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
featureMapPath,
|
|
21
|
+
isFeatureMapFolder,
|
|
22
|
+
type EngineConfig,
|
|
23
|
+
} from '../config/engine-config'
|
|
24
|
+
|
|
25
|
+
/** Built-in tools no agent gets, and secret files no agent reads. */
|
|
26
|
+
export const BASE_DISALLOWED_TOOLS = [
|
|
27
|
+
'Workflow',
|
|
28
|
+
'Skill',
|
|
29
|
+
'WebFetch',
|
|
30
|
+
'WebSearch',
|
|
31
|
+
'Monitor',
|
|
32
|
+
'PowerShell',
|
|
33
|
+
'NotebookEdit',
|
|
34
|
+
'SendMessage',
|
|
35
|
+
'Artifact',
|
|
36
|
+
'RemoteTrigger',
|
|
37
|
+
'CronCreate',
|
|
38
|
+
'PushNotification',
|
|
39
|
+
'SendUserFile',
|
|
40
|
+
'EnterWorktree',
|
|
41
|
+
'ExitWorktree',
|
|
42
|
+
'ListMcpResourcesTool',
|
|
43
|
+
'ReadMcpResourceTool',
|
|
44
|
+
'Bash(run_in_background:true)',
|
|
45
|
+
'Read(~/.claude.json)',
|
|
46
|
+
'Read(~/.claude/**)',
|
|
47
|
+
'Read(~/.paseo/**)',
|
|
48
|
+
'Read(~/.ssh/**)',
|
|
49
|
+
'Read(~/.config/gh/**)',
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Deny rules every role gets, so the package install and what it writes are
|
|
54
|
+
* shut at the SDK's permission layer too. `bunx` is left to the guard hook:
|
|
55
|
+
* a config's type check may run through it.
|
|
56
|
+
*/
|
|
57
|
+
export const INSTALL_DENY_RULES = [
|
|
58
|
+
...['install', 'i', 'add', 'remove', 'update', 'pm'].flatMap((sub) => [
|
|
59
|
+
`Bash(bun ${sub})`,
|
|
60
|
+
`Bash(bun ${sub} *)`,
|
|
61
|
+
]),
|
|
62
|
+
...PACKAGE_MANAGERS.flatMap((name) => [`Bash(${name})`, `Bash(${name} *)`]),
|
|
63
|
+
'Edit(node_modules/**)',
|
|
64
|
+
'Edit(**/node_modules/**)',
|
|
65
|
+
...LOCKFILES.flatMap((name) => [`Edit(${name})`, `Edit(**/${name})`]),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
const readOnlyRules = (): string[] => [
|
|
69
|
+
'Bash(ls)',
|
|
70
|
+
'Bash(ls *)',
|
|
71
|
+
'Bash(cat *)',
|
|
72
|
+
'Bash(head *)',
|
|
73
|
+
'Bash(tail *)',
|
|
74
|
+
'Bash(wc *)',
|
|
75
|
+
'Bash(grep *)',
|
|
76
|
+
'Bash(rg *)',
|
|
77
|
+
'Bash(find *)',
|
|
78
|
+
'Bash(pwd)',
|
|
79
|
+
'Bash(sed -n *)',
|
|
80
|
+
...GIT_READ_SUBCOMMANDS.flatMap((sub) => [
|
|
81
|
+
`Bash(git ${sub})`,
|
|
82
|
+
`Bash(git ${sub} *)`,
|
|
83
|
+
]),
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A role's SDK permission settings. Under `dontAsk` only pre-approved calls
|
|
88
|
+
* run, so `allowed` lists exactly what the role may do; `disallowed` shuts
|
|
89
|
+
* the rest; `tools` is the only built-in tools the agent sees. The guard hook
|
|
90
|
+
* (`checkToolCall`) still checks every call these let through.
|
|
91
|
+
*/
|
|
92
|
+
export const permissionRules = ({
|
|
93
|
+
role,
|
|
94
|
+
may_edit_tests,
|
|
95
|
+
config,
|
|
96
|
+
}: GuardArgs & {
|
|
97
|
+
config: EngineConfig
|
|
98
|
+
}): { tools: string[]; allowed: string[]; disallowed: string[] } => {
|
|
99
|
+
const commands = checkCommands({ role, config })
|
|
100
|
+
const shell = role === 'learner' ? [] : readOnlyRules()
|
|
101
|
+
// A bun test command takes extra args (a file, `-t`) only when it is one
|
|
102
|
+
// plain command; one with shell syntax runs as its exact copy.
|
|
103
|
+
const testRules = commands.tests.flatMap(({ run, results }) =>
|
|
104
|
+
results !== 'bun' || wordsOf(run) === null
|
|
105
|
+
? [`Bash(${run})`]
|
|
106
|
+
: [`Bash(${run})`, `Bash(${run} *)`]
|
|
107
|
+
)
|
|
108
|
+
const checks = [
|
|
109
|
+
...testRules,
|
|
110
|
+
...commands.others.map((command) => `Bash(${command})`),
|
|
111
|
+
]
|
|
112
|
+
const setup = config.test_setup_files.map((path) => `Edit(${path})`)
|
|
113
|
+
const tests = config.test_file_patterns.map((glob) => `Edit(${glob})`)
|
|
114
|
+
// The test helpers are the test-writer's too (#619).
|
|
115
|
+
const helpers = (config.test_helper_patterns ?? []).map(
|
|
116
|
+
(glob) => `Edit(${glob})`
|
|
117
|
+
)
|
|
118
|
+
// The feature map is the test-writer's too (#583): the file, or every
|
|
119
|
+
// page of a folder map (#597).
|
|
120
|
+
const map_path = featureMapPath({ config })
|
|
121
|
+
const map =
|
|
122
|
+
map_path === null
|
|
123
|
+
? []
|
|
124
|
+
: isFeatureMapFolder({ path: map_path })
|
|
125
|
+
? [`Edit(${map_path}/**/*.md)`]
|
|
126
|
+
: [`Edit(${map_path})`]
|
|
127
|
+
const writes: Record<
|
|
128
|
+
GuardRole,
|
|
129
|
+
{ allowed: string[]; disallowed: string[] }
|
|
130
|
+
> = {
|
|
131
|
+
'test-writer': may_edit_tests
|
|
132
|
+
? {
|
|
133
|
+
allowed: [...tests, ...helpers, ...map, 'Bash(rm *)'],
|
|
134
|
+
disallowed: setup,
|
|
135
|
+
}
|
|
136
|
+
: { allowed: [], disallowed: ['Edit', 'Write'] },
|
|
137
|
+
implementer: {
|
|
138
|
+
allowed: ['Edit(**)', 'Bash(rm *)'],
|
|
139
|
+
disallowed: may_edit_tests ? setup : [...tests, ...setup],
|
|
140
|
+
},
|
|
141
|
+
reviewer: { allowed: [], disallowed: ['Edit', 'Write'] },
|
|
142
|
+
learner: { allowed: [], disallowed: ['Edit', 'Write', 'Bash'] },
|
|
143
|
+
}
|
|
144
|
+
// Every role may start read-only `searcher` helpers (#627).
|
|
145
|
+
return {
|
|
146
|
+
tools: [...TOOLS[role], HELPER_TOOL],
|
|
147
|
+
allowed: [
|
|
148
|
+
...READ_TOOLS,
|
|
149
|
+
`${HELPER_TOOL}(${HELPER_TYPE})`,
|
|
150
|
+
...writes[role].allowed,
|
|
151
|
+
...checks,
|
|
152
|
+
...shell,
|
|
153
|
+
'mcp__luca',
|
|
154
|
+
],
|
|
155
|
+
disallowed: [
|
|
156
|
+
...BASE_DISALLOWED_TOOLS,
|
|
157
|
+
...INSTALL_DENY_RULES,
|
|
158
|
+
...writes[role].disallowed,
|
|
159
|
+
],
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { checkToolCall, type GuardRole } from './role-rules'
|
|
2
|
+
|
|
3
|
+
import type { EngineConfig } from '../config/engine-config'
|
|
4
|
+
|
|
5
|
+
/** Seam 3: what the guard rule tests share: a config, a worktree, and `decide`. */
|
|
6
|
+
|
|
7
|
+
export const CONFIG: EngineConfig = {
|
|
8
|
+
checks: {
|
|
9
|
+
test: 'bun test',
|
|
10
|
+
types: 'bunx --bun tsc --noEmit',
|
|
11
|
+
lint: 'bun run lint',
|
|
12
|
+
},
|
|
13
|
+
test_file_patterns: ['src/**/*.test.ts'],
|
|
14
|
+
test_setup_files: ['src/test-setup.ts'],
|
|
15
|
+
rule_files: [],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** `CONFIG` with a feature map (#583). */
|
|
19
|
+
export const WITH_MAP: EngineConfig = {
|
|
20
|
+
...CONFIG,
|
|
21
|
+
feature_map: 'docs/feature-map.md',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const WORKTREE = '/runs/run-1/tickets/11'
|
|
25
|
+
|
|
26
|
+
/** `may_edit_tests` as the engine sets it on a normal (not refactor) ticket. */
|
|
27
|
+
export const usual = (role: GuardRole): boolean => role === 'test-writer'
|
|
28
|
+
|
|
29
|
+
export const decide = ({
|
|
30
|
+
role,
|
|
31
|
+
may_edit_tests,
|
|
32
|
+
tool_name,
|
|
33
|
+
tool_input,
|
|
34
|
+
}: {
|
|
35
|
+
role: GuardRole
|
|
36
|
+
/** Defaults to `usual(role)`. */
|
|
37
|
+
may_edit_tests?: boolean
|
|
38
|
+
tool_name: string
|
|
39
|
+
tool_input: unknown
|
|
40
|
+
}): boolean =>
|
|
41
|
+
checkToolCall({
|
|
42
|
+
role,
|
|
43
|
+
may_edit_tests: may_edit_tests ?? usual(role),
|
|
44
|
+
tool_name,
|
|
45
|
+
tool_input,
|
|
46
|
+
worktree: WORKTREE,
|
|
47
|
+
config: CONFIG,
|
|
48
|
+
}).allow
|