Skip to content

Let admins list and inspect workspaces, audited - #1026

Open
Maximo-Guk wants to merge 29 commits into
mainfrom
maximo/admin-api
Open

Maximo-Guk wants to merge 29 commits into
mainfrom
maximo/admin-api

Conversation

@Maximo-Guk

@Maximo-Guk Maximo-Guk commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

Builds on #1040, now merged. Lets a deployment admin list users and workspaces and inspect any workspace read-only, with every call recorded in the audit log, and shows those calls in the Audit tab with their sensitivity. Supersedes #861.

Kernel (first commit)

  • AdminApi.listUsers / listWorkspaces, read from the user directory
  • AdminApi.inspectWorkspace(id, reason, { access, lifetimeSeconds }) mints a read-only AdminWorkspaceView (describe, listChats, getChat, listActions, listFiles, readFile, listGatekeepers, getDiagnostics) through OverseerDurableObject.openForAdmin, which is reachable only over the DO binding. The view:
    • skips collaborator and observer checks by explicit policy (docs/observers.md); the audit is the compensating control
    • re-checks admin status on every call, and expires
  • Access levels off / metadata / full. A view runs at the lower of the level asked for (metadata by default) and the deployment's, which is capped by ADMIN_WORKSPACE_ACCESS_MAX (default metadata; anything unrecognised means off).
  • Every view call is recorded as an AdminAccessRecord after the server-side read and before anything leaves the server. The record says what the answer exposed (metadata or content) and who owns the workspace (workspaceOwner, resolved when the view opens)

UI (second commit)

  • The log's row is Time · Admin · Action · Sensitivity · Target user. The expanded row shows the workspace, reason, level, arguments and Via.
    • Sensitivity is Content read (the answer held content), Workspace metadata or Setting change.
    • Refused marks a call the access level blocked, so it read nothing. It's either Content withheld (a Metadata-only view asked for chat messages, file contents or descriptions) or Access off (the deployment's level is Off). Refused calls are still logged.
    • Target user is the owner of the workspace the call reached. Setting changes have none.
  • New filters: sensitivity, target user (click one in the log, or type it), and workspace (picked from a record's details, shown as a removable chip). The Action filter lists the workspace calls.
  • Most active admins shows each admin's content reads

More workspace reads (last nine commits)

  • Chats: getChatState (the recorded turn, queued agent calls, pending actions), listChatAttachments, readChatAttachment
  • Code: getCommitLog, listFilesAtCommit, readFileAtCommit, listChangedPaths, listPendingChanges, readPendingFile, at any commit the workspace stores
  • Sharing: listObservers, listObserverRequirements, listShareLinks, listAutoApprovals, listBlueprints
  • listGatekeepers adds who created each connection (recorded from now on, so older ones have none), and getDiagnostics the storage size and who is present
  • Same rules as the first eight: recorded before answering, exposed set from the answer, and only stored objects are read, never a connection. Below full the three read* calls are refused, and commit messages, queued-call argument summaries and blueprint ids are withheld.
  • No UI change beyond the Audit tab's "Metadata only" help sentence; the Action filter lists the new calls.

Screenshots (three admins, real inspections at full)

Before (main): settings changes only.

Activity log on main: settings changes only

After: content reads per admin; the log with a content read opened; carol's getChat calls, a refused read opened; page 2.

Admin actions with content reads, and Most active admins with a Content reads column Activity log with Sensitivity and Target user columns and a content read's details Log filtered to getChat and target user carol, a refused read's details, and the workspace access level Page 2 of the activity log

@github-actions github-actions Bot added workshop/frontend Changes to the Workshop frontend kernel Changes to the Workshop kernel workshop/shared Changes to shared Workshop APIs labels Nov 5, 2026
@ask-bonk

ask-bonk Bot commented Nov 5, 2026

Copy link
Copy Markdown

LGTM!

github run

@ask-bonk

ask-bonk Bot commented Nov 5, 2026

Copy link
Copy Markdown

LGTM!

github run

@github-actions github-actions Bot deleted a comment from ask-bonk Bot Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown

Preview: pr1026-maximo-admin-api

https://pr1026-maximo-admin-api-router.cloudflare-os-previews.workers.dev

Dashboard · deleted when this PR closes

@ask-bonk

ask-bonk Bot commented Nov 5, 2026

Copy link
Copy Markdown

LGTM!

github run

...(refused ? {refused} : {}),
};
try {
await impl.adminAudits.getByName("").append(record, policy.retentionDays);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Prevent stale KV retention from pruning the audit log

#call() builds this policy from readAdminConfig()'s eventually consistent KV mirror, but append() immediately calls #prune() with the supplied retention. After an admin raises retention from 90 to 365 days, a workspace still seeing 90 can permanently delete records that cross the 90-day boundary during propagation, even though they are now within the committed retention. I reproduced this with the actual audit-store class and in-memory SQLite: a record retained when the setting changed was deleted by an inspection append 20 seconds later. Keep destructive pruning on the existing authoritative-config paths (listAdminAudit() / getAdminAuditStats()), or otherwise prevent inspection appends from pruning with stale retention. The package guide identifies the source of truth: “The AdminSettings durable object owns the authoritative AdminConfig” (packages/workshop-backend/AGENTS.md).

@ask-bonk

ask-bonk Bot commented Nov 5, 2026

Copy link
Copy Markdown

Review: 1 finding.

Submitted one actionable inline comment, verified with an in-memory reproduction.

github run

@ask-bonk

ask-bonk Bot commented Nov 5, 2026

Copy link
Copy Markdown

LGTM!

github run

@github-actions github-actions Bot deleted a comment from ask-bonk Bot Nov 5, 2026
@ask-bonk

ask-bonk Bot commented Nov 5, 2026

Copy link
Copy Markdown

LGTM!

github run

@github-actions github-actions Bot deleted a comment from ask-bonk Bot Nov 5, 2026
@ask-bonk

ask-bonk Bot commented Nov 5, 2026

Copy link
Copy Markdown

LGTM!

github run

@github-actions github-actions Bot deleted a comment from ask-bonk Bot Nov 5, 2026
@github-actions github-actions Bot deleted a comment from ask-bonk Bot Nov 5, 2026
@Maximo-Guk
Maximo-Guk added this pull request to stack #1027 November 5, 2026 14:46
Maximo-Guk and others added 24 commits November 9, 2026 16:58
Clear filters reset the filters, but text typed in the admin or owner field and not applied stayed,
and Enter then applied it. The fields now drop it whenever every filter is cleared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TypedFilterField hid what was typed when its filter changed from elsewhere, and brought it back
when the filter came back to the value it was typed over. It now drops it as soon as the filter,
or a clear of every filter, changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A username has no length limit, and workspaceOwner was kept whole, so a page of records naming a
very long owner could pass the RPC size limit and leave the Audit tab unable to load it. The store
now cuts the owner as it cuts the title, and the owner filter cuts its query the same way, so a long
owner's whole id still finds its records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cutting an owner's id like a title made two usernames sharing their first 255 characters one owner
in the log, and the owner filter mixed their records. An id over MAX_ADMIN_AUDIT_ARG_LENGTH is now
kept as its start, "…#" and 20 hex digits of the whole id's SHA-256 (auditOwnerId()), still
bounded, and the owner filter applies the same, taking the whole id or the kept one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The activity log gains a Target user column: for a call into a workspace, the workspace's owner,
the user the call reached, which filters the log by that user; a setting change reaches every user
and shows none. The owner filter is named Target user to match, and offers the target users the
shown page names. The column that held the workspace or the change is now Details, and the hidden
heading of the row toggles' column "Expand", so that no two columns share a name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The log's row is now time, admin, action, sensitivity and target user. The workspace or the
changes, and how the call came, move into the row's details, where Via joins them (the owner line
there went, the target user column holding it). The table sizes its columns to their content and
gives the action what is left, so on a narrow card the action is cut (whole in its tooltip) rather
than a name, and the log no longer needs a horizontal scrollbar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getAdminAuditStats() no longer ranks workspaces (topWorkspaces): the Audit tab stops showing them,
and listAdminAudit({ workspace }) lists every call into one, with its content reads through
`exposed`. The store drops the query that ranked and titled them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The card goes with the statistic behind it. A workspace is still chosen as a filter from a
record's details in the log, named by the title the log's records carry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It ordered the top workspaces as well until they were dropped; its name and fixtures now say only
what it checks, the top admins across both kinds of record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1040 landed resizable, remembered log columns, striped rows, a return to the newest page after a
settings change, focus kept on the filter field after a name is chosen in a row, and Escape
abandoning a half-typed filter. They now fit this log's columns: Time, Admin, Sensitivity and
Target user resize, and Action, rather than Changes, takes the width left over, so a narrow log
cuts the action while a name too long for its column wraps. Escape and the kept focus reach the
target user field as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ll's badges

Workspace metadata and Refused wrapped onto two lines in the Sensitivity column's default width.
It starts at 240 px now, with Admin and Target user at 140, so the log still fits the Audit tab at
a 1440 px window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bjects alone

The git cache gains readers that answer from the objects it already stores and never pull:
localPathEntryAtCommit, readLocalFileAtCommitIfExists and changedLocalFilePathsBetween, with
readCommitLog taking stopAtMissing so a walk ends where the stored history does. The tree diff
takes its tree reader as an argument, so the local and the pulling diff share one walk. In the
overseer, buildChatContent, getCurrentChatContent and the worktree edit-base seeding take the
reader of a file's base text, which defaults to readFileAtCommit, and worktreeTouchedPaths counts
live rows beside the stored messages, so a caller that must not pull can fold a chat's code too.

Co-Authored-By: Claude Code <noreply@anthropic.com>
A connection's stored record carries createdBy, the author who created it. The session paths
(newGatekeeper, newAiModelGatekeeper and newAgentSpawnerGatekeeper) record the calling client's
profile, and createExternalResource records the initiator of the creating chat's own turn, leaving
the field out when that turn has no record. An ambient capsule is created by no one, so it has no
creator. Nothing reads the field yet; it is there so a connection can later be traced to a person.

Co-Authored-By: Claude Code <noreply@anthropic.com>
OverseerImpl gains presenceSnapshot(), blueprintSummaries() and shareLinkInfos(), the bodies that
addPresenceSubscriber(), listBlueprints() and listShareLinks() held inline, with the two private
blueprint helpers moving along. shareLinkInfos() takes the reader of the owner's profile as an
argument, so the client interface keeps passing its own whoami() and a caller that holds no client
session can supply another. The client methods delegate, and behaviour is unchanged.

Co-Authored-By: Claude Code <noreply@anthropic.com>
AdminWorkspaceView gains getChatState(), listChatAttachments() and readChatAttachment(), so an admin
can see why a chat looks stuck (its recorded turn, running or paused, its newest entry, the calls
queued for its agent and its pending actions) and what was attached to it. Each answer names its
fields one by one, since the stored records hold more: user ids, raw call arguments, scratch paths
and bytes. Below "full" a queued call's argument summary is emptied and readChatAttachment() is
refused as content, while an attachment's name, type, size and content hash stay, as titles do.
OverseerImpl.getChatAttachmentData() takes a `local` flag that reads a legacy attachment only from
the objects the workspace stores, so the view still never reads through a connection.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…ding changes

AdminWorkspaceView gains getCommitLog(), listFilesAtCommit(), readFileAtCommit(),
listChangedPaths(), listPendingChanges() and readPendingFile(), so an admin can see how a gadget's
code got to where it is and what a chat would change before it is accepted. Each read answers only
from the objects the workspace stores: a parent that is not stored ends the walk, and a worktree's
base is never pulled through its connection. The log's depth is bounded at
MAX_ADMIN_COMMIT_LOG_DEPTH. Below "full" the two file reads are refused as content and a commit
message is emptied, since it describes its change, while commit ids, authors and path names stay,
as file names do.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…is present

AdminWorkspaceView gains listObservers(), listObserverRequirements(), listShareLinks(),
listAutoApprovals() and listBlueprints(), so an admin can see who a workspace is shared with, what
sharing it asks of a recipient and which actions its members opted to apply without a manual
approval. Each read answers from the workspace's own records and never reaches a connection, and a
share link never carries a key that redeems it. Below "full" a blueprint's id is emptied and its
screenshot URL left out, since the id is a share link to its code, while titles, descriptions and
link notes stay, as a workspace's title does. listGatekeepers() adds each connection's creator, and
getDiagnostics() the storage size and the presence roster, which an admin view never joins.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The admin inspection suite seeds a workspace with a merged gadget, a chat attachment and a draft
chat whose pending changes include a file just over MAX_BUFFERED_BLOB_SIZE, then calls every read
AdminWorkspaceView offers and checks both the answer and the audit record it leaves, including the
large file read to its end as a stream. It pins what is withheld below "full": attachment, commit
and pending file reads are refused as content and recorded with their arguments, commit messages
are emptied, and an invalid log depth is rejected with no record. It also checks that a connection
names its creator only when a member made it, that admitted and refused observers are both listed,
that a share link's key never appears in an answer, and that the presence roster holds the owner
and never the admin.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…it tab's help

docs/admin-access.md lists every AdminWorkspaceView read in the interface's groups, with the caveats
for listObservers(), listPendingChanges() and listAutoApprovals(), and says what shows of a commit a
connection supplied and who is named as a connection's creator. Its account of a call names the
content methods refused below "full", the failed reads that are recorded, the unrecorded log depth
and how a `stream` answer is audited, and the levels, exposure table, recorded refusals, error codes
and tests follow. docs/observers.md extends the admin exception to attachments, commit messages and
connection-supplied repositories, and the Audit tab's help sentence says what "Metadata only"
withholds, with a test, so an admin reads the same rule where the level is chosen.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…folded

listPendingChanges() awaits the chat's folded content, then reads its pins and change rows. The
fold settles one microtask before the listing goes on, and an accept's synchronous tail can run in
that gap. The listing then held the files from before the accept against the pins the accept had
cleared, and reported every file as added, with no base. It reads the chat's code base and message
sequence on both sides of the fold and takes the fold again when they moved, as the fold itself
does while it builds.

Co-Authored-By: Claude Code <noreply@anthropic.com>
docs/observers.md listed the descriptions and fields of observations as restricted data. The view
withholds those of actions and hooks below "full" as well, as docs/admin-access.md says, so the
two documents name the same three.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The comment in AdminWorkspaceViewImpl.listPendingChanges() said the pins it reads are the ones the
content was folded from. That does not hold for a cached fold: a step that only commits in a
worktree pins it without moving the generation or the revision the cache is keyed by, so the
cached content has no entry for that worktree and the listing leaves it out, where a fresh fold
lists it with three empty lists. No path or base is reported wrongly. The comment says so; the
behaviour is unchanged.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The list of restricted data named messages, attachments, files, commit messages and the text of
actions, observations and hooks. An inspection also withholds the argument summaries of queued
agent calls and blueprint ids below "full". The list names them, and points at the table in
docs/admin-access.md that says what each call withholds.

Co-Authored-By: Claude Code <noreply@anthropic.com>

@clanky-the-clanker clanky-the-clanker Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approved — No findings. The one INFO from the last review (an incomplete restricted-data list in docs/observers.md) is fixed, and nothing changed since then has a defect.

The decision is advisory: this review is a comment and never approves, blocks or requests changes on the pull request.


✅ All checks passed

🔒 Security: ✅ LGTM

  • Access cap: the cap fails closed. An unrecognised ADMIN_WORKSPACE_ACCESS_MAX becomes off, and setAdminWorkspaceAccess refuses a level above the cap. Every call runs at the lower of the level asked for and the current policy.
  • Content refusal: below full, the five content reads are refused, and commit messages, queued-call argument summaries, the descriptions of actions, observations and hooks, and blueprint ids are withheld.
  • Recording order: the audit record is written before the answer leaves the server.
  • SQL: audit and directory queries bind every value as a parameter, and filter lengths are capped.

📖 AGENTS.md: ✅ LGTM

The AGENTS.md updates match the change.


📋 Code Quality: ✅ LGTM

  • View calls: AdminWorkspaceViewImpl.#call checks expiry and admin status before the read and again before it records the call, with no await between the last check and the audit append. A refused call is recorded and then refused. A failed read is recorded as metadata and rethrown. A call whose record cannot be written fails and returns nothing.
  • Local-only reads: the new git-cache readers use only stored objects and never pull. readCommitLog({ stopAtMissing }) marks a parent as reached before skipping it, so the walk never visits it twice.
  • Directory and paging: sync steps run one at a time and re-check the removed flag at each step. Pages fetch limit + 1 rows in id order, so no row is skipped between pages.
  • vitest.config.ts: ADMINS is now an array binding, which matches string[] | string in env.d.ts.

⚡ Performance: ✅ LGTM

Each admin view call does one config read and one audit append. Indexes cover the new filters, and the retry loop in listPendingChanges is bounded.


📝 Documentation: ✅ LGTM

The list of restricted data at docs/observers.md:118-120 now names the argument summaries of queued agent calls and blueprint ids, so it matches docs/admin-access.md.


Reviewed commit: 27434f04

@ask-bonk

ask-bonk Bot commented Nov 9, 2026

Copy link
Copy Markdown

LGTM!

github run

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown

Eval results

Verdict: ⚪ Unchanged. No task moved beyond what these runs can tell apart from noise.

Task Score Δ score Fisher test Cache hits Avg min Avg steps
change-calendar (gpt-6-luna) 100% → 10 run errors candidate run errors — 95% → — 2.8 → 0.3 24.9 → 1.7
chat-views (gpt-6-luna) 100% → 10 run errors candidate run errors — 94% → — 1.7 → 0.3 12.0 → 1.7
chess (gpt-6-luna) 50% → 10 run errors candidate run errors — 97% → — 3.6 → 0.4 27.2 → 1.8
compaction (claude-sonnet-5-5) 100% 0 pp p = 1.00 90%
0 pp
1.7 → 2.0 43.0
compaction (gpt-6-luna) 100% → 1 run error candidate run errors — 92% → — 1.8 → 0.1 43.0 → 0.0
incident-desk (gpt-6-luna) 90% → 10 run errors candidate run errors — 96% → — 3.0 → 0.4 28.1 → 2.2
ticket-triage-agent (gpt-6-luna) 100% → 100% (5 run errors) candidate run errors — 97% → — 0.2 → 0.1 2.2 → 1.5
worker-logs (gpt-6-luna) 90% → 10 run errors candidate run errors — 95% → — 3.1 → 0.3 21.9 → 1.3
Failed checks
Task Check Failed
chess (gpt-6-luna) t1 agrees-with-the-oracle-through-random-games 5/10 → —
chess (gpt-6-luna) t1 agrees-with-the-oracle-on-the-hard-positions 4/10 → —
chess (gpt-6-luna) t1 agrees-with-the-oracle-on-perft-positions 4/10 → —
chess (gpt-6-luna) t1 the-game-is-shared-across-connections 3/10 → —
chess (gpt-6-luna) t1 starts-from-the-standard-position 2/10 → —
incident-desk (gpt-6-luna) t1 opens-acknowledges-and-resolves-in-order 1/10 → —
incident-desk (gpt-6-luna) t1 simultaneous-acknowledges-yield-exactly-one-owner 1/10 → —
incident-desk (gpt-6-luna) t1 simultaneous-opens-of-one-id-admit-exactly-one 1/10 → —
incident-desk (gpt-6-luna) t1 board-lists-by-severity-then-age 1/10 → —
worker-logs (gpt-6-luna) t1 ingests-resets-and-summarises-per-worker 1/10 → —
worker-logs (gpt-6-luna) t1 hourly-buckets-cover-every-hour-including-empty-ones 1/10 → —
worker-logs (gpt-6-luna) t1 ranges-are-half-open-and-filter-by-worker 1/10 → —

Run · trajectories and raw results

@github-actions github-actions Bot deleted a comment from ask-bonk Bot Nov 9, 2026
@ask-bonk

ask-bonk Bot commented Nov 9, 2026

Copy link
Copy Markdown

🔬 Eval runs review

Performance

comparison.json reports unchanged, but GPT-6-Luna is non-comparable: ticket-triage-agent passed all five completed candidate runs versus main’s 10/10, while every candidate run of its other tasks aborted. The only clean comparison, Sonnet compaction, stayed at 100% with 43 model steps and 21 tool calls; cost was $1.4572 → $1.4584, time 1.7 → 2.0 minutes, cache hits 89.76% → 89.76%, and cache breaks 2.06% → 2.07% (both cache p-values 1). Infrastructure aborts account for the lost runs and apparent GPT cost/step savings, not more efficient completion.

🟡 VERDICT: INCONCLUSIVE

The diff’s audited admin views and directory feeds do not explain the opaque candidate-only GPT failures, while the single comparable Sonnet run provides too little evidence to clear or implicate this PR.

  • Agent execution aborts · caused by this PR: unclear — the agent prompt and tool definitions are unchanged, and the transcripts expose no underlying exception linking the failures to changed code.
Triage

Failure modes

  • Agent execution aborts · change-calendar 10/10, chat-views 10/10, chess 10/10, incident-desk 10/10, worker-logs 10/10, ticket-triage-agent 5/10, compaction (gpt-6-luna) 1/1 · harness bug · this PR: unclear — every aborted run reports only Sorry, something went wrong. GPT compaction stops on turn 1 before replying “Ready”; change-calendar trial 3 stops after a successful listBlueprints; chat-views trial 1 passes turns 1–4, then aborts on turn 5 after successful expense-gadget file writes. These are infrastructure interruptions, not rejected task results; main has no infrastructure errors.

Tool errors

  • editFile: validation failed · change-calendar 2 → 0, chess 1 → 0, incident-desk 6 → 0, worker-logs 3 → 0 · model error — main’s agents omitted filename, supplied text or .replace instead of required fields, or passed a batch wrapper directly to editFile; corrected calls succeeded.
  • editFile: no matching text · change-calendar 1 → 0, chess 3 → 0, incident-desk 1 → 0, worker-logs 3 → 1 · model error — replacement anchors did not match the written source. Candidate worker-logs trial 6, turn 1 double-escaped the regex in its anchor; main change-calendar trial 2 recovered by rereading and quoting the actual text.
  • editFile: multiple matches · chess 2 → 1, incident-desk 2 → 0 · model error — anchors were not unique despite the parameter’s explicit requirement. Candidate chess trial 5, turn 1 matched both knight and king logic, then successfully retried with the surrounding blocks.
  • editFile: file not found · change-calendar 1 → 0, chess 1 → 0 · model error — main change-calendar trial 3, turn 4 used CHANGE_CALENDAR/README.md instead of README.md; chess trial 6, turn 3 supplied CHESS as the filename instead of a source file.
  • grep: invalid regex, unmatched ) · chess 1 → 0 · model error — main chess trial 9, turn 1 searched for castling).*test; retrying with castling succeeded.

What to do

  • Rerun the GPT evaluations with sanitized underlying error diagnostics, then refresh artifacts/candidate/trajectories.md and artifacts/comparison/comparison.json before attributing the aborts to this PR.
  • No changes to packages/workshop-backend/src/agent.ts are warranted for the existing editFile and grep model mistakes.

github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kernel Changes to the Workshop kernel workshop/frontend Changes to the Workshop frontend workshop/shared Changes to shared Workshop APIs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant