refactor(session): consolidate mutable properties

This commit is contained in:
Dax Raad
2026-09-15 18:50:43 -04:00
parent 12e53ee6d4
commit 547d60183d
27 changed files with 144 additions and 375 deletions
+2 -2
View File
@@ -147,7 +147,7 @@ Review endpoints in document order. For each endpoint, select one disposition an
| [x] 053 | `POST` | `/api/session/{sessionID}/fork` | `session.fork` | Change | Request now accepts optional branded `before` message ID; omission copies full history. |
| [x] 054 | `POST` | `/api/session/{sessionID}/agent` | `session.switchAgent` | Keep | Subsequent-execution agent selection retained. |
| [x] 055 | `POST` | `/api/session/{sessionID}/model` | `session.switchModel` | Keep | Subsequent-execution model and optional variant selection retained. |
| [x] 056 | `PATCH` | `/api/session/{sessionID}` | `session.rename` | Change | Title-only rename now uses the session resource path. |
| [x] 056 | `PATCH` | `/api/session/{sessionID}` | `session.update` | Change | General session patch now updates optional title and permissions. |
| [x] 057 | `POST` | `/api/session/{sessionID}/move` | `session.move` | Change | Removed inaccurate local-change transfer claim; delivery behavior retained. |
| [x] 058 | `POST` | `/api/session/{sessionID}/background` | `session.background` | Keep | Backgroundable foreground tools transition to background observation; idle requests remain no-ops. |
@@ -213,7 +213,7 @@ Review endpoints in document order. For each endpoint, select one disposition an
| [x] 098 | `GET` | `/api/session/{sessionID}/permission` | `session.permission.list` | Keep | Pending session permission list retained. |
| [x] 099 | `GET` | `/api/session/{sessionID}/permission/{requestID}` | `session.permission.get` | Keep | Specific pending permission read with ownership validation retained. |
| [x] 100 | `POST` | `/api/session/{sessionID}/permission/{requestID}/reply` | `session.permission.reply` | Change | Renamed request field from `reply` to `decision`. |
| [ ] 101 | `PUT` | `/api/session/{sessionID}/permission/rules` | `session.permission.rules` | | |
| [x] 101 | — | — | — | Remove | Permission rules are updated through `session.update`. |
## Group 8: Filesystem, worktrees, and VCS
@@ -146,7 +146,7 @@ export function createHomeSessionsController(home: HomeController) {
const next = title.trim()
if (!next || next === sessionLabel(session)) return true
return ctx.sdk.api.session
.rename({ sessionID: session.id, title: next })
.update({ sessionID: session.id, title: next })
.then(() => {
ctx.data.session.remember({ ...(ctx.data.session.get(session.id) ?? session), title: next })
// Rename advances time.updated server-side; re-sync the canonical
@@ -149,7 +149,7 @@ export function createTimelineController(input: { session: TimelineSessionSource
if (!next || next === (titleLabel() ?? "")) return true
setPending("rename", true)
const success = await serverSDK.api.session
.rename({ sessionID: id, title: next })
.update({ sessionID: id, title: next })
.then(() => true)
.catch((error) => {
showToast({ title: language.t("common.requestFailed"), description: errorMessage(error) })
@@ -116,7 +116,7 @@ function SessionTabEntry(props: {
ctx.data.session.remember({ ...value, title })
try {
await ctx.sdk.api.session.rename({ sessionID: value.id, title })
await ctx.sdk.api.session.update({ sessionID: value.id, title })
} catch (err) {
const current = session()
const currentCtx = props.serverCtx
+1 -1
View File
@@ -128,7 +128,7 @@ async function execute(input: RunCommandInput, prepared: Prepared, endpoint: End
const model = target.model ? { providerID: target.model.providerID, modelID: target.model.id } : undefined
const variant = target.model?.variant
if (!target.resume && input.title !== undefined) {
await client.session.rename({
await client.session.update({
sessionID: target.session.id,
title: input.title || prepared.message.slice(0, 50) + (prepared.message.length > 50 ? "..." : ""),
})
+8 -11
View File
@@ -233,9 +233,13 @@ export type SessionSwitchModelOperation<E = never> = (
input: SessionSwitchModelInput,
) => Effect.Effect<SessionSwitchModelOutput, E>
export type SessionRenameInput = { readonly sessionID: Session.ID; readonly title: string }
export type SessionRenameOutput = void
export type SessionRenameOperation<E = never> = (input: SessionRenameInput) => Effect.Effect<SessionRenameOutput, E>
export type SessionUpdateInput = {
readonly sessionID: Session.ID
readonly title?: string | undefined
readonly permissions?: Permission.Ruleset | undefined
}
export type SessionUpdateOutput = void
export type SessionUpdateOperation<E = never> = (input: SessionUpdateInput) => Effect.Effect<SessionUpdateOutput, E>
export type SessionMoveInput = {
readonly sessionID: Session.ID
@@ -1386,7 +1390,7 @@ export interface SessionApi<E = never> {
readonly fork: SessionForkOperation<E>
readonly switchAgent: SessionSwitchAgentOperation<E>
readonly switchModel: SessionSwitchModelOperation<E>
readonly rename: SessionRenameOperation<E>
readonly update: SessionUpdateOperation<E>
readonly move: SessionMoveOperation<E>
readonly prompt: SessionPromptOperation<E>
readonly command: SessionCommandOperation<E>
@@ -1789,12 +1793,6 @@ export type PermissionReplyOperation<E = never> = (
input: PermissionReplyInput,
) => Effect.Effect<PermissionReplyOutput, E>
export type PermissionRulesInput = { readonly sessionID: Session.ID; readonly permissions: Permission.Ruleset }
export type PermissionRulesOutput = void
export type PermissionRulesOperation<E = never> = (
input: PermissionRulesInput,
) => Effect.Effect<PermissionRulesOutput, E>
export interface PermissionApi<E = never> {
readonly request: { readonly list: PermissionRequestListOperation<E> }
readonly saved: { readonly list: PermissionSavedListOperation<E>; readonly remove: PermissionSavedRemoveOperation<E> }
@@ -1802,7 +1800,6 @@ export interface PermissionApi<E = never> {
readonly list: PermissionListOperation<E>
readonly get: PermissionGetOperation<E>
readonly reply: PermissionReplyOperation<E>
readonly rules: PermissionRulesOperation<E>
}
export type FileListInput = {
+9 -19
View File
@@ -39,8 +39,8 @@ import type {
SessionSwitchAgentOutput,
SessionSwitchModelInput,
SessionSwitchModelOutput,
SessionRenameInput,
SessionRenameOutput,
SessionUpdateInput,
SessionUpdateOutput,
SessionMoveInput,
SessionMoveOutput,
SessionPromptInput,
@@ -174,8 +174,6 @@ import type {
PermissionGetOutput,
PermissionReplyInput,
PermissionReplyOutput,
PermissionRulesInput,
PermissionRulesOutput,
FileListInput,
FileListOutput,
FileFindInput,
@@ -446,11 +444,12 @@ const EndpointSessionSwitchModel = (raw: RawClient["server.session"]) => (input:
),
)
const EndpointSessionRename = (raw: RawClient["server.session"]) => (input: SessionRenameInput) =>
preserveEffect<SessionRenameOutput>()(
raw["session.rename"]({ params: { sessionID: input["sessionID"] }, payload: { title: input["title"] } }).pipe(
Effect.mapError(mapClientError),
),
const EndpointSessionUpdate = (raw: RawClient["server.session"]) => (input: SessionUpdateInput) =>
preserveEffect<SessionUpdateOutput>()(
raw["session.update"]({
params: { sessionID: input["sessionID"] },
payload: { title: input["title"], permissions: input["permissions"] },
}).pipe(Effect.mapError(mapClientError)),
)
const EndpointSessionMove = (raw: RawClient["server.session"]) => (input: SessionMoveInput) =>
@@ -745,7 +744,7 @@ const adaptGroupSession = (raw: RawClient["server.session"]) => ({
fork: EndpointSessionFork(raw),
switchAgent: EndpointSessionSwitchAgent(raw),
switchModel: EndpointSessionSwitchModel(raw),
rename: EndpointSessionRename(raw),
update: EndpointSessionUpdate(raw),
move: EndpointSessionMove(raw),
prompt: EndpointSessionPrompt(raw),
command: EndpointSessionCommand(raw),
@@ -1112,14 +1111,6 @@ const EndpointPermissionReply = (raw: RawClient["server.permission"]) => (input:
}).pipe(Effect.mapError(mapClientError)),
)
const EndpointPermissionRules = (raw: RawClient["server.permission"]) => (input: PermissionRulesInput) =>
preserveEffect<PermissionRulesOutput>()(
raw["session.permission.rules"]({
params: { sessionID: input["sessionID"] },
payload: { permissions: input["permissions"] },
}).pipe(Effect.mapError(mapClientError)),
)
const adaptGroupPermission = (raw: RawClient["server.permission"]) => ({
request: { list: EndpointPermissionRequestList(raw) },
saved: { list: EndpointPermissionSavedList(raw), remove: EndpointPermissionSavedRemove(raw) },
@@ -1127,7 +1118,6 @@ const adaptGroupPermission = (raw: RawClient["server.permission"]) => ({
list: EndpointPermissionList(raw),
get: EndpointPermissionGet(raw),
reply: EndpointPermissionReply(raw),
rules: EndpointPermissionRules(raw),
})
const EndpointFileList = (raw: RawClient["server.fs"]) => (input?: FileListInput) =>
@@ -33,8 +33,8 @@ import type {
SessionSwitchAgentOutput,
SessionSwitchModelInput,
SessionSwitchModelOutput,
SessionRenameInput,
SessionRenameOutput,
SessionUpdateInput,
SessionUpdateOutput,
SessionMoveInput,
SessionMoveOutput,
SessionPromptInput,
@@ -168,8 +168,6 @@ import type {
PermissionGetOutput,
PermissionReplyInput,
PermissionReplyOutput,
PermissionRulesInput,
PermissionRulesOutput,
FileReadInput,
FileReadOutput,
FileListInput,
@@ -640,12 +638,12 @@ export function make(options: ClientOptions) {
},
requestOptions,
),
rename: (input: SessionRenameInput, requestOptions?: RequestOptions) =>
request<SessionRenameOutput>(
update: (input: SessionUpdateInput, requestOptions?: RequestOptions) =>
request<SessionUpdateOutput>(
{
method: "PATCH",
path: `/api/session/${encodeURIComponent(input.sessionID)}`,
body: { title: input["title"] },
body: { title: input["title"], permissions: input["permissions"] },
successStatus: 204,
declaredStatuses: [400, 401, 404],
empty: true,
@@ -1519,18 +1517,6 @@ export function make(options: ClientOptions) {
},
requestOptions,
),
rules: (input: PermissionRulesInput, requestOptions?: RequestOptions) =>
request<PermissionRulesOutput>(
{
method: "PUT",
path: `/api/session/${encodeURIComponent(input.sessionID)}/permission/rules`,
body: { permissions: input["permissions"] },
successStatus: 204,
declaredStatuses: [400, 401, 404],
empty: true,
},
requestOptions,
),
},
file: {
read: (input: FileReadInput, requestOptions?: RequestOptions) =>
+14 -16
View File
@@ -3903,12 +3903,23 @@ export type SessionSwitchModelInput = {
export type SessionSwitchModelOutput = void
export type SessionRenameInput = {
export type SessionUpdateInput = {
readonly sessionID: { readonly sessionID: string }["sessionID"]
readonly title: { readonly title: string }["title"]
readonly title?: {
readonly title?: string | undefined
readonly permissions?:
| ReadonlyArray<{ readonly action: string; readonly resource: string; readonly effect: "allow" | "deny" | "ask" }>
| undefined
}["title"]
readonly permissions?: {
readonly title?: string | undefined
readonly permissions?:
| ReadonlyArray<{ readonly action: string; readonly resource: string; readonly effect: "allow" | "deny" | "ask" }>
| undefined
}["permissions"]
}
export type SessionRenameOutput = void
export type SessionUpdateOutput = void
export type SessionMoveInput = {
readonly sessionID: { readonly sessionID: string }["sessionID"]
@@ -5721,19 +5732,6 @@ export type PermissionReplyInput = {
export type PermissionReplyOutput = void
export type PermissionRulesInput = {
readonly sessionID: { readonly sessionID: string }["sessionID"]
readonly permissions: {
readonly permissions: ReadonlyArray<{
readonly action: string
readonly resource: string
readonly effect: "allow" | "deny" | "ask"
}>
}["permissions"]
}
export type PermissionRulesOutput = void
export type FileReadInput = {
readonly location?: { readonly location?: { readonly directory?: string | undefined } | undefined }["location"]
readonly path: string
+6 -2
View File
@@ -415,7 +415,6 @@ export const make = Effect.fn("PluginHost.make")(function* (
: Effect.fail(new Error(`Permission request not found: ${input.requestID}`)),
),
),
rules: sessions.setPermissions,
},
plugin: {
list: () => response(plugin.list()),
@@ -532,7 +531,12 @@ export const make = Effect.fn("PluginHost.make")(function* (
prompt: sessions.prompt,
generate: (input) => sessions.generate(input).pipe(Effect.map((text) => ({ text }))),
command: (input) => sessions.command({ ...input, command: input.name }),
rename: sessions.rename,
update: Effect.fn(function* (input) {
yield* sessions.get(input.sessionID)
if (input.title !== undefined) yield* sessions.rename({ sessionID: input.sessionID, title: input.title })
if (input.permissions !== undefined)
yield* sessions.setPermissions({ sessionID: input.sessionID, permissions: input.permissions })
}),
move: sessions.move,
synthetic: sessions.synthetic,
interrupt: (input) =>
+1 -1
View File
@@ -51,7 +51,7 @@ export const Plugin = {
const sessionID = input.sessionID ?? context.sessionID
const title = input.title.trim()
if (!title) return Effect.fail(new ToolFailure({ message: "Session title must not be empty" }))
return ctx.session.rename({ sessionID, title }).pipe(
return ctx.session.update({ sessionID, title }).pipe(
Effect.as({
output: { sessionID, title },
content: `Renamed session ${sessionID} to ${title}.`,
+1 -1
View File
@@ -19,6 +19,6 @@ export interface PermissionHooks {
readonly evaluate: PermissionEvaluation
}
export type PermissionDomain = Pick<PermissionApi<unknown>, "list" | "get" | "reply" | "rules"> & {
export type PermissionDomain = Pick<PermissionApi<unknown>, "list" | "get" | "reply"> & {
readonly hook: Hooks<PermissionHooks>
}
+1 -1
View File
@@ -134,7 +134,7 @@ export type SessionDomain = Pick<
| "command"
| "synthetic"
| "interrupt"
| "rename"
| "update"
| "move"
| "wait"
| "context"
+1 -2
View File
@@ -439,7 +439,6 @@ export function fromPromise(plugin: Plugin) {
list: adaptApiMethod(PermissionEndpoints["session.permission.list"], host.permission.list),
get: adaptApiMethod(PermissionEndpoints["session.permission.get"], host.permission.get),
reply: adaptApiMethod(PermissionEndpoints["session.permission.reply"], host.permission.reply),
rules: adaptApiMethod(PermissionEndpoints["session.permission.rules"], host.permission.rules),
},
plugin: {
list: adaptApiMethod(PluginEndpoints["plugin.list"], host.plugin.list),
@@ -581,7 +580,7 @@ export function fromPromise(plugin: Plugin) {
command: adaptApiMethod(SessionEndpoints["session.command"], host.session.command),
synthetic: adaptApiMethod(SessionEndpoints["session.synthetic"], host.session.synthetic),
interrupt: adaptApiMethod(SessionEndpoints["session.interrupt"], host.session.interrupt),
rename: adaptApiMethod(SessionEndpoints["session.rename"], host.session.rename),
update: adaptApiMethod(SessionEndpoints["session.update"], host.session.update),
move: adaptApiMethod(SessionEndpoints["session.move"], host.session.move),
wait: adaptApiMethod(SessionEndpoints["session.wait"], host.session.wait),
context: adaptApiMethod(SessionEndpoints["session.context"], host.session.context),
+1 -1
View File
@@ -19,6 +19,6 @@ export interface PermissionHooks {
readonly evaluate: PermissionEvaluation
}
export type PermissionDomain = Pick<PermissionApi, "list" | "get" | "reply" | "rules"> & {
export type PermissionDomain = Pick<PermissionApi, "list" | "get" | "reply"> & {
readonly hook: Hooks<PermissionHooks>
}
+1 -1
View File
@@ -134,7 +134,7 @@ export type SessionDomain = Pick<
| "command"
| "synthetic"
| "interrupt"
| "rename"
| "update"
| "move"
| "wait"
| "context"
+21 -84
View File
@@ -1438,7 +1438,7 @@
},
"patch": {
"tags": ["session"],
"operationId": "session.rename",
"operationId": "session.update",
"parameters": [
{
"name": "sessionID",
@@ -1493,8 +1493,8 @@
}
}
},
"description": "Update the session title.",
"summary": "Rename session",
"description": "Update mutable session properties.",
"summary": "Update session",
"requestBody": {
"content": {
"application/json": {
@@ -1502,10 +1502,26 @@
"type": "object",
"properties": {
"title": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"permissions": {
"anyOf": [
{
"$ref": "#/components/schemas/Permission.Ruleset"
},
{
"type": "null"
}
]
}
},
"required": ["title"],
"additionalProperties": false
}
}
@@ -8075,85 +8091,6 @@
}
}
},
"/api/session/{sessionID}/permission/rules": {
"put": {
"tags": ["permission"],
"operationId": "session.permission.rules",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"pattern": "^ses"
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
},
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
}
]
}
}
}
}
},
"description": "Replace the session-scoped permission rules. Rules are evaluated after the agent's rules, and the last matching rule wins.",
"summary": "Replace session permission rules",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"permissions": {
"$ref": "#/components/schemas/Permission.Ruleset"
}
},
"required": ["permissions"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/fs/read/*": {
"get": {
"tags": ["filesystem"],
@@ -132,21 +132,4 @@ export const makePermissionGroup = <
}),
),
)
.add(
HttpApiEndpoint.put("session.permission.rules", "/api/session/:sessionID/permission/rules", {
params: { sessionID: Session.ID },
payload: Schema.Struct({ permissions: Permission.Ruleset }),
success: HttpApiSchema.NoContent,
error: SessionNotFoundError,
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "session.permission.rules",
summary: "Replace session permission rules",
description:
"Replace the session-scoped permission rules. Rules are evaluated after the agent's rules, and the last matching rule wins.",
}),
),
)
.annotateMerge(OpenApi.annotations({ title: "permission", description: "Experimental permission routes." }))
+8 -5
View File
@@ -355,18 +355,21 @@ export const makeSessionGroup = <
),
)
.add(
HttpApiEndpoint.patch("session.rename", "/api/session/:sessionID", {
HttpApiEndpoint.patch("session.update", "/api/session/:sessionID", {
params: { sessionID: Session.ID },
payload: Schema.Struct({ title: Schema.String }),
payload: Schema.Struct({
title: Schema.String.pipe(Schema.optional),
permissions: Permission.Ruleset.pipe(Schema.optional),
}),
success: HttpApiSchema.NoContent,
error: SessionNotFoundError,
})
.middleware(sessionLocationMiddleware)
.annotateMerge(
OpenApi.annotations({
identifier: "session.rename",
summary: "Rename session",
description: "Update the session title.",
identifier: "session.update",
summary: "Update session",
description: "Update mutable session properties.",
}),
),
)
+1 -1
View File
@@ -83,7 +83,7 @@ it.live(
Effect.gen(function* () {
executed.push(generation)
yield* ctx.session
.rename({ sessionID: tool.sessionID, title: `${key}:${generation}` })
.update({ sessionID: tool.sessionID, title: `${key}:${generation}` })
.pipe(Effect.orDie)
return { output: key, content: key }
}),
@@ -83,15 +83,6 @@ export const PermissionHandler = HttpApiBuilder.group(Api, "server.permission",
return HttpApiSchema.NoContent.make()
}),
)
.handle(
"session.permission.rules",
Effect.fn(function* (ctx) {
yield* sessions
.setPermissions({ sessionID: ctx.params.sessionID, permissions: ctx.payload.permissions })
.pipe(Effect.catchTag("Session.NotFoundError", missingSession))
return HttpApiSchema.NoContent.make()
}),
)
.handle(
"permission.saved.list",
Effect.fn(function* (ctx) {
+15 -8
View File
@@ -253,16 +253,23 @@ export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handl
}),
)
.handle(
"session.rename",
"session.update",
Effect.fn(function* (ctx) {
if (ctx.payload.title) {
yield* session
.rename({ sessionID: ctx.params.sessionID, title: ctx.payload.title })
.pipe(Effect.catchTag("Session.NotFoundError", missingSession))
return HttpApiSchema.NoContent.make()
yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession))
if (ctx.payload.title !== undefined) {
if (ctx.payload.title) {
yield* session
.rename({ sessionID: ctx.params.sessionID, title: ctx.payload.title })
.pipe(Effect.catchTag("Session.NotFoundError", missingSession))
} else {
const title = yield* SessionTitle.Service
yield* title.generate(ctx.params.sessionID)
}
}
const title = yield* SessionTitle.Service
yield* title.generate(ctx.params.sessionID)
if (ctx.payload.permissions !== undefined)
yield* session
.setPermissions({ sessionID: ctx.params.sessionID, permissions: ctx.payload.permissions })
.pipe(Effect.catchTag("Session.NotFoundError", missingSession))
return HttpApiSchema.NoContent.make()
}),
)
@@ -18,7 +18,7 @@ export function DialogSessionRename(props: { sessionID: string; currentTitle?: s
const title = value.trim()
if (!title) return
void client.api.session
.rename({ sessionID: props.sessionID, title })
.update({ sessionID: props.sessionID, title })
.then(() => dialog.clear())
.catch((error) =>
toast.show({
+1 -1
View File
@@ -37,7 +37,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
if (generatingTitles[sessionID]) return
setGeneratingTitles(sessionID, true)
await client.api.session
.rename({ sessionID, title: "" })
.update({ sessionID, title: "" })
.then(() => {
// The HTTP response can beat the renamed event. Keep pending until the new title is projected locally.
data.session.invalidate(sessionID)
+1 -1
View File
@@ -821,7 +821,7 @@ export function Session(props: {
const title = input.trim()
void (
title
? client.api.session.rename({ sessionID: route.sessionID, title })
? client.api.session.update({ sessionID: route.sessionID, title })
: data.session.title.generate(route.sessionID)
).catch((error) => toast.error(error))
},
+21 -84
View File
@@ -1438,7 +1438,7 @@
},
"patch": {
"tags": ["session"],
"operationId": "session.rename",
"operationId": "session.update",
"parameters": [
{
"name": "sessionID",
@@ -1493,8 +1493,8 @@
}
}
},
"description": "Update the session title.",
"summary": "Rename session",
"description": "Update mutable session properties.",
"summary": "Update session",
"requestBody": {
"content": {
"application/json": {
@@ -1502,10 +1502,26 @@
"type": "object",
"properties": {
"title": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"permissions": {
"anyOf": [
{
"$ref": "#/components/schemas/Permission.Ruleset"
},
{
"type": "null"
}
]
}
},
"required": ["title"],
"additionalProperties": false
}
}
@@ -8075,85 +8091,6 @@
}
}
},
"/api/session/{sessionID}/permission/rules": {
"put": {
"tags": ["permission"],
"operationId": "session.permission.rules",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"pattern": "^ses"
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
},
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
}
]
}
}
}
}
},
"description": "Replace the session-scoped permission rules. Rules are evaluated after the agent's rules, and the last matching rule wins.",
"summary": "Replace session permission rules",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"permissions": {
"$ref": "#/components/schemas/Permission.Ruleset"
}
},
"required": ["permissions"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/fs/read/*": {
"get": {
"tags": ["filesystem"],
+21 -84
View File
@@ -1438,7 +1438,7 @@
},
"patch": {
"tags": ["session"],
"operationId": "session.rename",
"operationId": "session.update",
"parameters": [
{
"name": "sessionID",
@@ -1493,8 +1493,8 @@
}
}
},
"description": "Update the session title.",
"summary": "Rename session",
"description": "Update mutable session properties.",
"summary": "Update session",
"requestBody": {
"content": {
"application/json": {
@@ -1502,10 +1502,26 @@
"type": "object",
"properties": {
"title": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"permissions": {
"anyOf": [
{
"$ref": "#/components/schemas/Permission.Ruleset"
},
{
"type": "null"
}
]
}
},
"required": ["title"],
"additionalProperties": false
}
}
@@ -8075,85 +8091,6 @@
}
}
},
"/api/session/{sessionID}/permission/rules": {
"put": {
"tags": ["permission"],
"operationId": "session.permission.rules",
"parameters": [
{
"name": "sessionID",
"in": "path",
"schema": {
"type": "string",
"pattern": "^ses"
},
"required": true
}
],
"security": [],
"responses": {
"204": {
"description": "<No Content>"
},
"400": {
"description": "InvalidRequestError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestErrorEncoded"
}
}
}
},
"401": {
"description": "UnauthorizedError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnauthorizedErrorEncoded"
}
}
}
},
"404": {
"description": "SessionNotFoundError",
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
},
{
"$ref": "#/components/schemas/SessionNotFoundErrorEncoded"
}
]
}
}
}
}
},
"description": "Replace the session-scoped permission rules. Rules are evaluated after the agent's rules, and the last matching rule wins.",
"summary": "Replace session permission rules",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"permissions": {
"$ref": "#/components/schemas/Permission.Ruleset"
}
},
"required": ["permissions"],
"additionalProperties": false
}
}
},
"required": true
}
}
},
"/api/fs/read/*": {
"get": {
"tags": ["filesystem"],