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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3382a025a7d0f1ce58e7a8a9f64a6a36705a2e27f2e9e9acd116dfa0c8955e8
4
- data.tar.gz: 13824a0d4d5680a880151780e33b611e16c2299f9c7465bebbf47af5bf32718b
3
+ metadata.gz: 0a2b2e4142b5c9c54b8d320d6e36c17832c2bfb69996fce034be52ee2b4849fe
4
+ data.tar.gz: 266f7d5ce81b724643c600582ce744ed48cb8bc24606d98c3b16bc21b515b673
5
5
  SHA512:
6
- metadata.gz: '098257f74f2a65053e822330315ce564e44fddef11ece7cb830364f8f1f8810a8161ae98a3cf439a35d54918b4bcca4670f97ac5f8ced351ed4e0cd8ae724ae2'
7
- data.tar.gz: 3051666fee426647550bf6abab8abd8d36f79ff8998ca90e03eb0df85b318044aa31389487270bba5a2340e1b48be846cde8253ef40e49e1cf23d0ba4b79b927
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
- # Shell access off: restrict to file tools โ€” the sandbox is the tool
129
- # list itself, not a request in the prompt.
130
- cmd += ["--tools", "Read,Glob,Grep,Edit,Write"] unless column.shell_access?
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
- unless column.shell_access?
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
- column.shell_access? ? EXECUTE_RULES : RESTRICTED_EXECUTE_RULES
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
- <p class="hint">Overrides this card's model and effort. Only model + effort โ€” budget, timeout, and concurrency are column policy. Takes effect on the next run segment (a run already going finishes on the model it started with).</p>
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>
@@ -0,0 +1,5 @@
1
+ class AddPermissionModeToCards < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :cards, :permission_mode, :string
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module Cardinal
2
- VERSION = "0.2.18"
2
+ VERSION = "0.2.19"
3
3
  end
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.18
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