cardinal-ai 0.2.18 โ 0.2.19
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.
- checksums.yaml +4 -4
- data/app/controllers/boards_controller.rb +2 -1
- data/app/controllers/cards_controller.rb +2 -1
- data/app/models/board.rb +9 -1
- data/app/models/card.rb +12 -0
- data/app/services/agent/runner.rb +13 -5
- data/app/views/boards/edit.html.erb +7 -1
- data/app/views/cards/_detail.html.erb +11 -1
- data/db/migrate/20260706002904_add_permission_mode_to_cards.rb +5 -0
- data/lib/cardinal/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0a2b2e4142b5c9c54b8d320d6e36c17832c2bfb69996fce034be52ee2b4849fe
|
|
4
|
+
data.tar.gz: 266f7d5ce81b724643c600582ce744ed48cb8bc24606d98c3b16bc21b515b673
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94358996801e25b84e34564156a362d3963bf4941c0d195215cf19aee86fd510f4d22ee1ecd3adf4500d2846c811f7df653d808cbceca8c028c38e678e4a3337
|
|
7
|
+
data.tar.gz: 86deb643dae9b203eeb462e0c62c30c3bb7aa85b648b89ef7c53b8436f3a14af013fa231384e1e5f28a74aa0be0e9bab410b22e820fd500af4fd4c4e0ec5ff42
|
|
@@ -29,8 +29,9 @@ class BoardsController < ApplicationController
|
|
|
29
29
|
|
|
30
30
|
def update
|
|
31
31
|
board = Board.first!
|
|
32
|
-
attrs = params.require(:board).permit(:name, :default_branch, archive_accepts_from: [])
|
|
32
|
+
attrs = params.require(:board).permit(:name, :default_branch, :permission_bypass, archive_accepts_from: [])
|
|
33
33
|
board.archive_accepts_from = attrs[:archive_accepts_from].to_a.map(&:to_s).reject(&:blank?) if attrs.key?(:archive_accepts_from)
|
|
34
|
+
board.permission_bypass = (attrs[:permission_bypass] == "1") if attrs.key?(:permission_bypass)
|
|
34
35
|
board.update!(
|
|
35
36
|
name: attrs[:name].presence || board.name,
|
|
36
37
|
default_branch: attrs[:default_branch].presence || board.default_branch
|
|
@@ -212,7 +212,8 @@ class CardsController < ApplicationController
|
|
|
212
212
|
end
|
|
213
213
|
|
|
214
214
|
def card_params
|
|
215
|
-
attrs = params.require(:card).permit(:title, :description, :tags, :branch_name, :pr_url, :summary, :compact, :model, :effort)
|
|
215
|
+
attrs = params.require(:card).permit(:title, :description, :tags, :branch_name, :pr_url, :summary, :compact, :model, :effort, :permission_mode)
|
|
216
|
+
attrs[:permission_mode] = attrs[:permission_mode].presence_in(Card::PERMISSION_MODES) if attrs.key?(:permission_mode)
|
|
216
217
|
attrs[:tags] = attrs[:tags].to_s.split(",").map(&:strip).reject(&:blank?) if attrs.key?(:tags)
|
|
217
218
|
# Blank model/effort from the "Use column default" option mean "no override" โ
|
|
218
219
|
# store nil so effective_* falls back to the column (card #33).
|
data/app/models/board.rb
CHANGED
|
@@ -55,7 +55,15 @@ class Board < ApplicationRecord
|
|
|
55
55
|
# may be DRAGGED onto the topbar ๐ (explicit-only, like column rails โ
|
|
56
56
|
# empty means the archive is not a drop target). The Advanced-panel archive
|
|
57
57
|
# button is always available; this governs only the drag affordance.
|
|
58
|
-
store_accessor :settings, :archive_accepts_from
|
|
58
|
+
store_accessor :settings, :archive_accepts_from, :permission_bypass
|
|
59
|
+
|
|
60
|
+
# Board default for worker autonomy (ยง permissions): true (default) lets
|
|
61
|
+
# agents act without asking โ full shell inside their workspace. false
|
|
62
|
+
# restricts workers to file tools (read/search/edit/write; Cardinal commits
|
|
63
|
+
# for them) unless a card explicitly overrides back to full autonomy.
|
|
64
|
+
def permission_bypass?
|
|
65
|
+
settings["permission_bypass"] != false
|
|
66
|
+
end
|
|
59
67
|
|
|
60
68
|
def archive_accepts?(column)
|
|
61
69
|
Array(archive_accepts_from).map(&:to_s).include?(column.id.to_s)
|
data/app/models/card.rb
CHANGED
|
@@ -92,6 +92,18 @@ class Card < ApplicationRecord
|
|
|
92
92
|
# default", so existing cards are unaffected. Read fresh at every segment
|
|
93
93
|
# spawn (start, restart, resume) by the runner and the planning assistant, so
|
|
94
94
|
# a change takes effect on the next segment โ never on an already-running one.
|
|
95
|
+
# Permission override (nil = board default): "bypass" forces full autonomy
|
|
96
|
+
# for this card, "ask" forces the restricted file-tools mode.
|
|
97
|
+
PERMISSION_MODES = ["bypass", "ask"].freeze
|
|
98
|
+
|
|
99
|
+
def effective_permission_bypass?
|
|
100
|
+
case permission_mode
|
|
101
|
+
when "bypass" then true
|
|
102
|
+
when "ask" then false
|
|
103
|
+
else board.permission_bypass?
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
95
107
|
def effective_model = model.presence || column.model
|
|
96
108
|
def effective_effort = effort.presence || column.effort
|
|
97
109
|
|
|
@@ -125,9 +125,10 @@ module Agent
|
|
|
125
125
|
cmd += ["--max-turns", "3", "--tools", ""]
|
|
126
126
|
else
|
|
127
127
|
cmd += ["--max-turns", (column.max_turns.presence || DEFAULT_EXECUTE_TURNS).to_s]
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
|
|
128
|
+
# Restricted agents (column shell toggle, or board/card permission
|
|
129
|
+
# setting) get file tools only โ the sandbox is the tool list itself,
|
|
130
|
+
# not a request in the prompt.
|
|
131
|
+
cmd += ["--tools", "Read,Glob,Grep,Edit,Write"] if restricted_tools?
|
|
131
132
|
end
|
|
132
133
|
# Effective (possibly card-overridden) config, read fresh here at spawn
|
|
133
134
|
# time โ so a mid-run override takes effect on the next segment (start,
|
|
@@ -232,7 +233,7 @@ module Agent
|
|
|
232
233
|
accumulate_usage(result)
|
|
233
234
|
# A shell-less agent cannot commit its own edits โ do it for it before
|
|
234
235
|
# any commit counting or salvage below.
|
|
235
|
-
|
|
236
|
+
if restricted_tools?
|
|
236
237
|
workspace.commit_all!("Card ##{card.number}: #{card.title.truncate(60)}\n\nCommitted by Cardinal for a shell-less agent.")
|
|
237
238
|
end
|
|
238
239
|
unless result[:success]
|
|
@@ -380,7 +381,14 @@ module Agent
|
|
|
380
381
|
end
|
|
381
382
|
|
|
382
383
|
def execute_rules
|
|
383
|
-
|
|
384
|
+
restricted_tools? ? RESTRICTED_EXECUTE_RULES : EXECUTE_RULES
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Three voices, one verdict: the column's shell toggle, the board's
|
|
388
|
+
# permission default, and the card's own override (which beats the board
|
|
389
|
+
# but never re-opens a column that turned shell off).
|
|
390
|
+
def restricted_tools?
|
|
391
|
+
!column.shell_access? || !card.effective_permission_bypass?
|
|
384
392
|
end
|
|
385
393
|
|
|
386
394
|
def plan_prompt
|
|
@@ -10,12 +10,18 @@
|
|
|
10
10
|
<div id="board-form-errors"></div>
|
|
11
11
|
|
|
12
12
|
<%= form_with model: @board, url: board_path(autosave: 1), class: "card-edit",
|
|
13
|
-
data: { autosave_target: "form" } do |f| %>
|
|
13
|
+
data: { autosave_target: "form", action: "input->autosave#save change->autosave#save" } do |f| %>
|
|
14
14
|
<label>Board name</label>
|
|
15
15
|
<%= f.text_field :name %>
|
|
16
16
|
|
|
17
17
|
<label>Default branch <%= info_tip("The branch agents fork card branches from and Done merges pull requests toward. Detected from the repo at first boot; fix it here if that guess was wrong.") %></label>
|
|
18
18
|
<%= f.text_field :default_branch, placeholder: "main" %>
|
|
19
|
+
|
|
20
|
+
<label class="check-row">
|
|
21
|
+
<%= f.check_box :permission_bypass, { checked: @board.permission_bypass? }, "1", "0" %>
|
|
22
|
+
Agents act without asking (full autonomy)
|
|
23
|
+
<%= info_tip("On (default): worker agents run shell commands โ tests, builds, git โ inside their workspace without permission prompts. Off: workers get file tools only (read, search, edit, write); they cannot execute anything, and Cardinal commits their edits for them. Each card can override this in its ๐ง panel; a column whose Shell access is off stays restricted regardless.") %>
|
|
24
|
+
</label>
|
|
19
25
|
<% end %>
|
|
20
26
|
|
|
21
27
|
<label>Repository</label>
|
|
@@ -176,7 +176,17 @@
|
|
|
176
176
|
<%= f.select :effort, [["Use column default", ""], "low", "medium", "high", "max"], selected: @card.effort %>
|
|
177
177
|
</div>
|
|
178
178
|
</div>
|
|
179
|
-
<
|
|
179
|
+
<div class="field-row">
|
|
180
|
+
<div>
|
|
181
|
+
<label>Permissions <%= info_tip("Overrides the board's autonomy default for THIS card. Full autonomy: the worker runs shell commands without asking. Restricted: file tools only โ it cannot execute anything, and Cardinal commits its edits. A column whose Shell access is off stays restricted regardless.") %></label>
|
|
182
|
+
<%= f.select :permission_mode,
|
|
183
|
+
[["Board default (#{@card.board.permission_bypass? ? "full autonomy" : "restricted"})", ""],
|
|
184
|
+
["Full autonomy โ acts without asking", "bypass"],
|
|
185
|
+
["Restricted โ file tools only", "ask"]],
|
|
186
|
+
selected: @card.permission_mode.to_s %>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
<p class="hint">Overrides this card's model, effort, and permissions. Budget, timeout, and concurrency are column policy. Takes effect on the next run segment (a run already going finishes on the settings it started with).</p>
|
|
180
190
|
</details>
|
|
181
191
|
<% end %>
|
|
182
192
|
</div>
|
data/lib/cardinal/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cardinal-ai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jason Ellis
|
|
@@ -280,6 +280,7 @@ files:
|
|
|
280
280
|
- db/migrate/20260705120001_add_issue_number_to_cards.rb
|
|
281
281
|
- db/migrate/20260705193935_add_settings_to_boards.rb
|
|
282
282
|
- db/migrate/20260705225451_add_asana_url_to_cards.rb
|
|
283
|
+
- db/migrate/20260706002904_add_permission_mode_to_cards.rb
|
|
283
284
|
- db/queue_schema.rb
|
|
284
285
|
- db/seeds.rb
|
|
285
286
|
- docker/agent/Dockerfile
|