@capybara-hq/kernel 0.1.0-rc.20 → 0.1.0-rc.22
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/bootstrap.json +10 -1
- package/dist/agents/admin-agents.d.ts +16 -3
- package/dist/agents/admin-agents.js +65 -7
- package/dist/agents/agents.schemas.d.ts +24 -6
- package/dist/agents/agents.service.d.ts +4 -3
- package/dist/agents/agents.service.js +39 -26
- package/dist/agents/definition.d.ts +44 -6
- package/dist/agents/definition.js +86 -18
- package/dist/agents/from-template.d.ts +6 -3
- package/dist/agents/from-template.js +57 -44
- package/dist/agents/org-templates.js +97 -41
- package/dist/agents/templates.d.ts +6 -0
- package/dist/agents/templates.js +15 -1
- package/dist/catalog/admin-catalog.controller.d.ts +2 -4
- package/dist/catalog/admin-catalog.controller.js +2 -6
- package/dist/catalog/catalog.module.js +3 -0
- package/dist/catalog/claude/report.d.ts +9 -2
- package/dist/catalog/claude/report.js +1 -0
- package/dist/catalog/claude/translate.js +1 -1
- package/dist/catalog/component-promotion.js +1 -2
- package/dist/catalog/component-state.d.ts +13 -1
- package/dist/catalog/component-state.js +14 -5
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/marketplaces.service.js +2 -1
- package/dist/catalog/org-catalog.controller.d.ts +13 -3
- package/dist/catalog/org-catalog.controller.js +26 -4
- package/dist/catalog/org-catalog.schemas.d.ts +13 -0
- package/dist/catalog/org-catalog.schemas.js +34 -0
- package/dist/catalog/org-plugin-uploads.controller.js +0 -1
- package/dist/catalog/org-plugin-uploads.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.js +0 -3
- package/dist/catalog/org-plugin-uploads.schemas.d.ts +0 -1
- package/dist/catalog/org-plugin-uploads.schemas.js +0 -2
- package/dist/catalog/platform-components.d.ts +0 -1
- package/dist/catalog/platform-components.js +3 -8
- package/dist/catalog/plugin-import.d.ts +0 -8
- package/dist/catalog/plugin-import.js +4 -35
- package/dist/catalog/plugin-install.d.ts +2 -2
- package/dist/catalog/plugin-install.js +2 -2
- package/dist/catalog/plugin-package.d.ts +7 -0
- package/dist/catalog/plugin-package.js +27 -1
- package/dist/catalog/plugin-upgrade.d.ts +5 -6
- package/dist/catalog/plugin-upgrade.js +5 -23
- package/dist/catalog/requests.service.d.ts +66 -10
- package/dist/catalog/requests.service.js +196 -15
- package/dist/channels/actions.d.ts +2 -0
- package/dist/channels/actions.js +19 -4
- package/dist/channels/cards.d.ts +16 -2
- package/dist/channels/cards.js +40 -7
- package/dist/channels/channels.module.js +3 -0
- package/dist/channels/outbound.d.ts +22 -1
- package/dist/channels/outbound.js +116 -7
- package/dist/channels/phrases.d.ts +25 -0
- package/dist/channels/phrases.js +19 -0
- package/dist/channels/threads.d.ts +12 -0
- package/dist/channels/threads.js +34 -0
- package/dist/config/schema.d.ts +23 -1
- package/dist/config/schema.js +105 -5
- package/dist/conversations/conversations.service.d.ts +42 -9
- package/dist/conversations/conversations.service.js +57 -25
- package/dist/conversations/events.d.ts +106 -11
- package/dist/conversations/events.js +121 -18
- package/dist/conversations/message-context.d.ts +6 -3
- package/dist/conversations/message-context.js +14 -10
- package/dist/conversations/queue.d.ts +15 -2
- package/dist/conversations/queue.js +22 -3
- package/dist/conversations/queue.service.d.ts +12 -6
- package/dist/conversations/queue.service.js +36 -13
- package/dist/db/kernel-schema.d.ts +105 -3
- package/dist/devices/conversation-devices.d.ts +8 -1
- package/dist/devices/conversation-devices.js +8 -0
- package/dist/devices/devices.controller.d.ts +21 -4
- package/dist/devices/devices.controller.js +46 -8
- package/dist/devices/devices.schemas.d.ts +9 -1
- package/dist/devices/devices.schemas.js +11 -3
- package/dist/devices/devices.service.d.ts +176 -4
- package/dist/devices/devices.service.js +341 -6
- package/dist/devices/dto.d.ts +56 -3
- package/dist/devices/dto.js +51 -1
- package/dist/devices/public-suffix.d.ts +6 -0
- package/dist/devices/public-suffix.js +79 -0
- package/dist/errors.d.ts +46 -5
- package/dist/errors.js +46 -5
- package/dist/evals/reconstruct.d.ts +18 -13
- package/dist/evals/reconstruct.js +50 -36
- package/dist/evals/recorded-tools.d.ts +7 -7
- package/dist/evals/recorded-tools.js +4 -3
- package/dist/evals/trajectory.d.ts +6 -1
- package/dist/evals/trajectory.js +7 -0
- package/dist/extensions/bootstrap.d.ts +49 -10
- package/dist/extensions/bootstrap.js +104 -27
- package/dist/extensions/package.js +2 -2
- package/dist/http/api.module.js +5 -0
- package/dist/inbox/inbox.module.js +2 -1
- package/dist/inbox/inbox.service.d.ts +19 -4
- package/dist/inbox/inbox.service.js +14 -3
- package/dist/infra/config/kernel-config.service.js +11 -0
- package/dist/infra/events/kernel-events.d.ts +2 -1
- package/dist/lifecycle/kernel-lifecycle.d.ts +5 -1
- package/dist/lifecycle/kernel-lifecycle.js +16 -4
- package/dist/local-devices/device-policy.service.d.ts +65 -0
- package/dist/local-devices/device-policy.service.js +217 -0
- package/dist/local-devices/local-device-gateway.js +4 -1
- package/dist/local-devices/local-device-provider.d.ts +40 -3
- package/dist/local-devices/local-device-provider.js +109 -7
- package/dist/local-devices/local-devices.module.d.ts +2 -1
- package/dist/local-devices/local-devices.module.js +19 -3
- package/dist/local-devices/machines.d.ts +14 -3
- package/dist/local-devices/machines.js +41 -3
- package/dist/local-devices/network-grants.d.ts +104 -0
- package/dist/local-devices/network-grants.js +288 -0
- package/dist/local-devices/policy-summary.d.ts +20 -0
- package/dist/local-devices/policy-summary.js +63 -0
- package/dist/orgs/orgs.module.js +4 -10
- package/dist/orgs/roles.d.ts +1 -1
- package/dist/orgs/roles.js +4 -0
- package/dist/orgs/settings.controller.js +2 -1
- package/dist/orgs/settings.service.d.ts +164 -12
- package/dist/orgs/settings.service.js +212 -24
- package/dist/platform/exclusions.js +11 -1
- package/dist/platform/operation.js +4 -0
- package/dist/platform/ops/admin-items.ops.js +1 -1
- package/dist/platform/ops/agent-skills.ops.js +2 -2
- package/dist/platform/ops/agent-templates.ops.js +2 -2
- package/dist/platform/ops/agents.ops.js +11 -15
- package/dist/platform/ops/devices.ops.js +3 -1
- package/dist/platform/ops/inbox.ops.js +19 -0
- package/dist/platform/ops/org-settings.ops.js +14 -5
- package/dist/platform/ops/platform.ops.d.ts +9 -1
- package/dist/platform/ops/platform.ops.js +34 -12
- package/dist/platform/ops/requests.ops.js +3 -2
- package/dist/platform/ops/schedules.ops.d.ts +21 -0
- package/dist/platform/ops/schedules.ops.js +470 -0
- package/dist/platform/ops/skills.ops.js +3 -3
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +5 -0
- package/dist/platform/route-schemas.js +3 -0
- package/dist/roles/worker.module.js +3 -0
- package/dist/runtime/context/build.js +13 -4
- package/dist/runtime/context/compaction.js +6 -3
- package/dist/runtime/context/device-paths.d.ts +43 -21
- package/dist/runtime/context/device-paths.js +104 -21
- package/dist/runtime/context/run-context.d.ts +9 -0
- package/dist/runtime/context/run-context.js +8 -0
- package/dist/runtime/context/skills.d.ts +14 -1
- package/dist/runtime/context/skills.js +36 -5
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +16 -5
- package/dist/runtime/run-finish.d.ts +6 -1
- package/dist/runtime/run-finish.js +16 -2
- package/dist/runtime/run-queue.js +21 -2
- package/dist/runtime/run-worker.js +14 -33
- package/dist/runtime/runtime.module.js +3 -0
- package/dist/runtime/subagents/definition.d.ts +32 -14
- package/dist/runtime/subagents/definition.js +106 -109
- package/dist/runtime/subagents/dispatch.js +4 -3
- package/dist/runtime/subagents/preamble.d.ts +2 -1
- package/dist/runtime/subagents/preamble.js +2 -1
- package/dist/runtime/waits.js +14 -4
- package/dist/sandbox/sandbox.service.d.ts +120 -10
- package/dist/sandbox/sandbox.service.js +335 -28
- package/dist/sandbox/tiers.d.ts +86 -0
- package/dist/sandbox/tiers.js +96 -0
- package/dist/schedules/effective-settings.d.ts +16 -0
- package/dist/schedules/effective-settings.js +17 -0
- package/dist/schedules/errors.d.ts +8 -0
- package/dist/schedules/errors.js +8 -0
- package/dist/schedules/fire.d.ts +118 -0
- package/dist/schedules/fire.js +625 -0
- package/dist/schedules/grant-rules.d.ts +94 -0
- package/dist/schedules/grant-rules.js +126 -0
- package/dist/schedules/job-ids.d.ts +32 -0
- package/dist/schedules/job-ids.js +27 -0
- package/dist/schedules/pause.d.ts +9 -0
- package/dist/schedules/pause.js +33 -0
- package/dist/schedules/run-tracker.d.ts +81 -0
- package/dist/schedules/run-tracker.js +333 -0
- package/dist/schedules/scan.job.d.ts +64 -0
- package/dist/schedules/scan.job.js +303 -0
- package/dist/schedules/schedule-math.d.ts +31 -0
- package/dist/schedules/schedule-math.js +223 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +351 -0
- package/dist/schedules/scheduled-tasks.service.js +1193 -0
- package/dist/schedules/schedules.controller.d.ts +68 -0
- package/dist/schedules/schedules.controller.js +242 -0
- package/dist/schedules/schedules.module.d.ts +16 -0
- package/dist/schedules/schedules.module.js +65 -0
- package/dist/schedules/schedules.schemas.d.ts +316 -0
- package/dist/schedules/schedules.schemas.js +186 -0
- package/dist/schedules/store.d.ts +81 -0
- package/dist/schedules/store.js +390 -0
- package/dist/schedules/store.module.d.ts +6 -0
- package/dist/schedules/store.module.js +21 -0
- package/dist/schedules/types.d.ts +108 -0
- package/dist/schedules/types.js +1 -0
- package/dist/schedules/unattended.d.ts +117 -0
- package/dist/schedules/unattended.js +228 -0
- package/dist/setup/oauth-token-probe.js +8 -4
- package/dist/skills/agent-skills.service.d.ts +4 -8
- package/dist/skills/agent-skills.service.js +18 -53
- package/dist/skills/catalog.d.ts +65 -62
- package/dist/skills/catalog.js +109 -206
- package/dist/skills/conversation-catalog.d.ts +3 -3
- package/dist/skills/conversation-catalog.js +5 -12
- package/dist/skills/invoke.d.ts +2 -2
- package/dist/skills/skills.controller.d.ts +3 -1
- package/dist/skills/skills.service.d.ts +38 -37
- package/dist/skills/skills.service.js +168 -107
- package/dist/skills/usable.d.ts +57 -8
- package/dist/skills/usable.js +172 -38
- package/dist/tools/approvals.controller.d.ts +4 -2
- package/dist/tools/approvals.controller.js +17 -2
- package/dist/tools/approvals.schemas.d.ts +1 -0
- package/dist/tools/approvals.schemas.js +3 -1
- package/dist/tools/approvals.service.d.ts +182 -7
- package/dist/tools/approvals.service.js +597 -13
- package/dist/tools/builtin-names.d.ts +25 -4
- package/dist/tools/builtin-names.js +45 -4
- package/dist/tools/builtins/builtin-tools.d.ts +8 -0
- package/dist/tools/builtins/builtin-tools.js +23 -2
- package/dist/tools/builtins/sandbox/bash.js +16 -7
- package/dist/tools/builtins/sandbox/common.d.ts +1 -1
- package/dist/tools/builtins/sandbox/common.js +33 -10
- package/dist/tools/builtins/sandbox/edit.js +7 -3
- package/dist/tools/builtins/sandbox/file-state.d.ts +31 -1
- package/dist/tools/builtins/sandbox/file-state.js +33 -3
- package/dist/tools/builtins/sandbox/write.js +8 -4
- package/dist/tools/builtins/schedules.d.ts +107 -0
- package/dist/tools/builtins/schedules.js +789 -0
- package/dist/tools/elicitation.d.ts +2 -0
- package/dist/tools/elicitation.js +1 -1
- package/dist/tools/fixed-ask.d.ts +7 -0
- package/dist/tools/fixed-ask.js +9 -0
- package/dist/tools/network-asks.d.ts +109 -0
- package/dist/tools/network-asks.js +221 -0
- package/dist/tools/pipeline.d.ts +53 -6
- package/dist/tools/pipeline.js +202 -13
- package/dist/tools/policy.d.ts +21 -8
- package/dist/tools/policy.js +63 -9
- package/dist/tools/registry.d.ts +10 -1
- package/dist/tools/tools.module.js +12 -0
- package/package.json +2 -2
- package/src/migrations/0113_scheduled_tasks.sql +108 -0
- package/src/migrations/0114_network_asks.sql +23 -0
- package/src/migrations/0115_network_requests.sql +28 -0
- package/src/migrations/0116_skills_default_on.sql +37 -0
- package/src/migrations/0117_sandbox_tiers.sql +11 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
2
2
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
3
|
-
import { ConversationsService, type RunControl } from "./conversations.service.js";
|
|
3
|
+
import { ConversationsService, type InternalMessageOptions, type RunControl } from "./conversations.service.js";
|
|
4
4
|
import { EventLog } from "./event-log.js";
|
|
5
5
|
import type { ConversationEvent } from "./events.js";
|
|
6
6
|
import { type QueueInput, type QueueItem, type QueuePauseReason } from "./queue.js";
|
|
@@ -15,7 +15,7 @@ export declare class ConversationQueue {
|
|
|
15
15
|
/** Starts the conversation's next run (`ensureRun`). */
|
|
16
16
|
private readonly runs;
|
|
17
17
|
readonly txHost: TransactionHost;
|
|
18
|
-
constructor(db: KernelDb, eventLog: Pick<EventLog, "appendIn" | "notify" | "publishChanged" | "findByIdem">, conversations: Pick<ConversationsService, "get" | "writable" | "resolveExtra" | "bindIn" | "checkPick" | "prepareQueuedSkill" | "
|
|
18
|
+
constructor(db: KernelDb, eventLog: Pick<EventLog, "appendIn" | "notify" | "publishChanged" | "findByIdem">, conversations: Pick<ConversationsService, "get" | "writable" | "resolveExtra" | "bindIn" | "checkPick" | "prepareQueuedSkill" | "skipWaitsIn" | "announceSkipped">,
|
|
19
19
|
/** Starts the conversation's next run (`ensureRun`). */
|
|
20
20
|
runs: Pick<RunControl, "ensureRun" | "wakeHeldTree">, txHost: TransactionHost);
|
|
21
21
|
list(orgId: string, conversationId: string, accountId: string): Promise<{
|
|
@@ -29,9 +29,11 @@ export declare class ConversationQueue {
|
|
|
29
29
|
* item, and uploaded files are bound to the conversation now, so sending later needs nobody's
|
|
30
30
|
* credentials. The same `clientMessageId` again returns the queued item (`created: false`), or,
|
|
31
31
|
* when it was already sent, the sent `user_message` with `item: null`. Then `ensureRun`: on an
|
|
32
|
-
* idle conversation the item goes out at once.
|
|
32
|
+
* idle conversation the item goes out at once. `internal` (kernel callers only, never HTTP): a
|
|
33
|
+
* scheduled task's marker and zone, stored with the item and written with its `user_message`
|
|
34
|
+
* (scheduled tasks spec §6).
|
|
33
35
|
*/
|
|
34
|
-
add(orgId: string, conversationId: string, accountId: string, input: QueueInput): Promise<{
|
|
36
|
+
add(orgId: string, conversationId: string, accountId: string, input: QueueInput, internal?: InternalMessageOptions): Promise<{
|
|
35
37
|
item: QueueItem | null;
|
|
36
38
|
event?: ConversationEvent;
|
|
37
39
|
created: boolean;
|
|
@@ -39,7 +41,9 @@ export declare class ConversationQueue {
|
|
|
39
41
|
private addIn;
|
|
40
42
|
/**
|
|
41
43
|
* Changes a queued item's text (its resources and files stay as queued). A skill invocation's
|
|
42
|
-
* text is its arguments: the skill is rendered again with them now.
|
|
44
|
+
* text is its arguments: the skill is rendered again with them now. A scheduled task's item
|
|
45
|
+
* becomes the person's message: its schedule marker (and the task's zone) is dropped, so the run
|
|
46
|
+
* it triggers is attended (scheduled tasks spec §7).
|
|
43
47
|
*/
|
|
44
48
|
edit(orgId: string, conversationId: string, accountId: string, itemId: string, text: string): Promise<QueueItem>;
|
|
45
49
|
private editIn;
|
|
@@ -48,7 +52,9 @@ export declare class ConversationQueue {
|
|
|
48
52
|
/**
|
|
49
53
|
* Sends a queued item now: it leaves the queue and is posted as a user message in the same
|
|
50
54
|
* transaction (an interjection when a run is active; the items before it keep their place).
|
|
51
|
-
* Racing the dispatcher, exactly one of them takes the item; the loser sees it gone (404).
|
|
55
|
+
* Racing the dispatcher, exactly one of them takes the item; the loser sees it gone (404). A
|
|
56
|
+
* scheduled task's item is sent as the person's message, without its schedule marker
|
|
57
|
+
* (scheduled tasks spec §7).
|
|
52
58
|
*/
|
|
53
59
|
sendNow(orgId: string, conversationId: string, accountId: string, itemId: string): Promise<ConversationEvent>;
|
|
54
60
|
private sendNowIn;
|
|
@@ -14,8 +14,9 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
14
14
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
15
15
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
16
16
|
import { RunControl as RunControlService } from "../runtime/run-control.js";
|
|
17
|
+
import { releaseQueuedTaskRunIn } from "../schedules/run-tracker.js";
|
|
17
18
|
import { notInvocable } from "../skills/invoke.js";
|
|
18
|
-
import { ConversationsService, } from "./conversations.service.js";
|
|
19
|
+
import { ConversationsService, NO_REPLY_SKIPS, } from "./conversations.service.js";
|
|
19
20
|
import { EventLog, payloadJson } from "./event-log.js";
|
|
20
21
|
import { userMessageIdemKey } from "./idem-keys.js";
|
|
21
22
|
import { alreadySent, appendQueuedIn, COLUMNS, checkText, itemNotFound, noNul, QUEUE_MAX_ITEMS, queuedSkillBlocked, queueFull, storedSkill, toItem, unpauseIfEmpty, } from "./queue.js";
|
|
@@ -67,9 +68,11 @@ let ConversationQueue = class ConversationQueue {
|
|
|
67
68
|
* item, and uploaded files are bound to the conversation now, so sending later needs nobody's
|
|
68
69
|
* credentials. The same `clientMessageId` again returns the queued item (`created: false`), or,
|
|
69
70
|
* when it was already sent, the sent `user_message` with `item: null`. Then `ensureRun`: on an
|
|
70
|
-
* idle conversation the item goes out at once.
|
|
71
|
+
* idle conversation the item goes out at once. `internal` (kernel callers only, never HTTP): a
|
|
72
|
+
* scheduled task's marker and zone, stored with the item and written with its `user_message`
|
|
73
|
+
* (scheduled tasks spec §6).
|
|
71
74
|
*/
|
|
72
|
-
async add(orgId, conversationId, accountId, input) {
|
|
75
|
+
async add(orgId, conversationId, accountId, input, internal = {}) {
|
|
73
76
|
const conv = await this.conversations.writable(orgId, conversationId, accountId);
|
|
74
77
|
const files = input.files ?? [];
|
|
75
78
|
checkText(input.text, files.length, input.attachments?.length ?? 0, input.skill !== undefined);
|
|
@@ -92,7 +95,7 @@ let ConversationQueue = class ConversationQueue {
|
|
|
92
95
|
const skill = invoked === undefined || input.skill === undefined
|
|
93
96
|
? undefined
|
|
94
97
|
: storedSkill(input.skill.name, invoked);
|
|
95
|
-
const out = await this.addIn(orgId, conversationId, accountId, input, files, skill, extra);
|
|
98
|
+
const out = await this.addIn(orgId, conversationId, accountId, input, files, skill, extra, internal);
|
|
96
99
|
if (out === null) {
|
|
97
100
|
return ((await this.prior(conversationId, input.clientMessageId)) ?? { item: null, created: false });
|
|
98
101
|
}
|
|
@@ -103,7 +106,7 @@ let ConversationQueue = class ConversationQueue {
|
|
|
103
106
|
return { item: toItem(out.row), created: out.created };
|
|
104
107
|
}
|
|
105
108
|
// its own transaction: the caller notifies and publishes after the commit
|
|
106
|
-
async addIn(orgId, conversationId, accountId, input, files, skill, extra) {
|
|
109
|
+
async addIn(orgId, conversationId, accountId, input, files, skill, extra, internal) {
|
|
107
110
|
const t = this.db.db;
|
|
108
111
|
await t
|
|
109
112
|
.selectFrom("conversations")
|
|
@@ -154,6 +157,14 @@ let ConversationQueue = class ConversationQueue {
|
|
|
154
157
|
model: input.model === undefined ? null : JSON.stringify(input.model),
|
|
155
158
|
effort: input.effort ?? null,
|
|
156
159
|
skill: skill?.skill ?? null,
|
|
160
|
+
schedule: internal.schedule === undefined
|
|
161
|
+
? null
|
|
162
|
+
: JSON.stringify({
|
|
163
|
+
...internal.schedule,
|
|
164
|
+
...(internal.timeZoneOverride !== undefined && {
|
|
165
|
+
timeZone: internal.timeZoneOverride,
|
|
166
|
+
}),
|
|
167
|
+
}),
|
|
157
168
|
})
|
|
158
169
|
.returning(COLUMNS)
|
|
159
170
|
.executeTakeFirstOrThrow();
|
|
@@ -161,7 +172,9 @@ let ConversationQueue = class ConversationQueue {
|
|
|
161
172
|
}
|
|
162
173
|
/**
|
|
163
174
|
* Changes a queued item's text (its resources and files stay as queued). A skill invocation's
|
|
164
|
-
* text is its arguments: the skill is rendered again with them now.
|
|
175
|
+
* text is its arguments: the skill is rendered again with them now. A scheduled task's item
|
|
176
|
+
* becomes the person's message: its schedule marker (and the task's zone) is dropped, so the run
|
|
177
|
+
* it triggers is attended (scheduled tasks spec §7).
|
|
165
178
|
*/
|
|
166
179
|
async edit(orgId, conversationId, accountId, itemId, text) {
|
|
167
180
|
const conv = await this.conversations.writable(orgId, conversationId, accountId);
|
|
@@ -189,13 +202,15 @@ let ConversationQueue = class ConversationQueue {
|
|
|
189
202
|
const t = this.db.db;
|
|
190
203
|
const cur = await t
|
|
191
204
|
.selectFrom("conversation_queue")
|
|
192
|
-
.select(["attachments", "files", "content", "skill"])
|
|
205
|
+
.select(["attachments", "files", "content", "skill", "schedule"])
|
|
193
206
|
.where("id", "=", itemId)
|
|
194
207
|
.where("conversation_id", "=", conversationId)
|
|
195
208
|
.forUpdate()
|
|
196
209
|
.executeTakeFirst();
|
|
197
210
|
if (!cur)
|
|
198
211
|
throw itemNotFound();
|
|
212
|
+
// a person's words now: the task's run for this item will not happen (scheduled tasks §6)
|
|
213
|
+
await releaseQueuedTaskRunIn(t, cur.schedule);
|
|
199
214
|
const skill = cur.skill;
|
|
200
215
|
checkText(text, cur.files.length, cur.attachments.length, skill !== null);
|
|
201
216
|
// an invocation's first block is its rendered text: replace it and the record
|
|
@@ -204,6 +219,8 @@ let ConversationQueue = class ConversationQueue {
|
|
|
204
219
|
.updateTable("conversation_queue")
|
|
205
220
|
.set({
|
|
206
221
|
text: noNul(text),
|
|
222
|
+
// edited by a person: no longer the task's message
|
|
223
|
+
schedule: null,
|
|
207
224
|
updated_at: sql `now()`,
|
|
208
225
|
...(stored !== undefined && {
|
|
209
226
|
skill: stored.skill,
|
|
@@ -229,16 +246,20 @@ let ConversationQueue = class ConversationQueue {
|
|
|
229
246
|
.deleteFrom("conversation_queue")
|
|
230
247
|
.where("id", "=", itemId)
|
|
231
248
|
.where("conversation_id", "=", conversationId)
|
|
232
|
-
.returning("id")
|
|
249
|
+
.returning(["id", "schedule"])
|
|
233
250
|
.executeTakeFirst();
|
|
234
251
|
if (!gone)
|
|
235
252
|
throw itemNotFound();
|
|
253
|
+
// a task's queued message deleted by a person: its run is skipped (scheduled tasks §6)
|
|
254
|
+
await releaseQueuedTaskRunIn(t, gone.schedule);
|
|
236
255
|
await unpauseIfEmpty(t, conversationId);
|
|
237
256
|
}
|
|
238
257
|
/**
|
|
239
258
|
* Sends a queued item now: it leaves the queue and is posted as a user message in the same
|
|
240
259
|
* transaction (an interjection when a run is active; the items before it keep their place).
|
|
241
|
-
* Racing the dispatcher, exactly one of them takes the item; the loser sees it gone (404).
|
|
260
|
+
* Racing the dispatcher, exactly one of them takes the item; the loser sees it gone (404). A
|
|
261
|
+
* scheduled task's item is sent as the person's message, without its schedule marker
|
|
262
|
+
* (scheduled tasks spec §7).
|
|
242
263
|
*/
|
|
243
264
|
async sendNow(orgId, conversationId, accountId, itemId) {
|
|
244
265
|
await this.conversations.writable(orgId, conversationId, accountId);
|
|
@@ -283,17 +304,19 @@ let ConversationQueue = class ConversationQueue {
|
|
|
283
304
|
if (!row)
|
|
284
305
|
throw itemNotFound();
|
|
285
306
|
const sent = await alreadySent(t, conversationId, row.client_message_id);
|
|
286
|
-
|
|
307
|
+
// sent by a person: their message, not the task's, whose run is skipped
|
|
308
|
+
await releaseQueuedTaskRunIn(t, row.schedule);
|
|
309
|
+
const out = await appendQueuedIn(t, this.eventLog, conversationId, { ...row, schedule: null });
|
|
287
310
|
await unpauseIfEmpty(t, conversationId);
|
|
288
311
|
// sent during the run, the item is the author's message like a direct send: it skips the
|
|
289
312
|
// pending questions they may answer (reply-while-question spec §4.2)
|
|
290
313
|
const skips = sent
|
|
291
|
-
? {
|
|
292
|
-
: await this.conversations.
|
|
314
|
+
? { skipped: NO_REPLY_SKIPS, toSeq: null }
|
|
315
|
+
: await this.conversations.skipWaitsIn(conversationId, row.account_id, out.event.seq);
|
|
293
316
|
return {
|
|
294
317
|
...out,
|
|
295
318
|
fresh: !sent,
|
|
296
|
-
skipped: skips.
|
|
319
|
+
skipped: skips.skipped,
|
|
297
320
|
toSeq: Math.max(out.event.seq, skips.toSeq ?? 0),
|
|
298
321
|
};
|
|
299
322
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { FileSystemHandle, ParsedTheme, SkillInvocation } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import type { FileSystemHandle, ParsedTheme, SandboxTier, SkillInvocation } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { ColumnType, Generated, Kysely } from "kysely";
|
|
3
|
+
import type { ModelRef } from "../agents/definition.js";
|
|
4
|
+
import type { ScheduleChat, ScheduleMarker, ScheduleMode, SchedulePauseReason, ScheduleStatus, ScheduleTrigger, TaskRunSkipReason, TaskRunStatus } from "../schedules/types.js";
|
|
3
5
|
/** 带默认值的时间戳列:插入可省略,读出为 Date。 */
|
|
4
6
|
type Ts = ColumnType<Date, Date | string | undefined, Date | string>;
|
|
5
7
|
type TsNullable = ColumnType<Date | null, Date | string | null | undefined, Date | string | null>;
|
|
@@ -259,6 +261,8 @@ export interface ConversationsTable {
|
|
|
259
261
|
subagent_depth: ColumnType<number, number | undefined, number>;
|
|
260
262
|
/** 0088, renamed in 0105: the device the conversation runs in; null = not chosen yet. */
|
|
261
263
|
device_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
264
|
+
/** 0113: the scheduled task that created it (`new` mode); null otherwise. */
|
|
265
|
+
scheduled_task_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
262
266
|
}
|
|
263
267
|
/** 0060: an org's order of file-preview@1 previewers (UI refresh B Task 11). */
|
|
264
268
|
export interface FilePreviewersTable {
|
|
@@ -297,6 +301,13 @@ export interface ConversationQueueTable {
|
|
|
297
301
|
effort: ColumnType<string | null, string | null | undefined, string | null>;
|
|
298
302
|
/** 0062: the `user_message.skill` record of a queued skill invocation, or null. */
|
|
299
303
|
skill: ColumnType<unknown, string | null | undefined, string | null>;
|
|
304
|
+
/**
|
|
305
|
+
* 0113: the schedule marker of a scheduled task's message, plus the task's zone (`timeZone`, the
|
|
306
|
+
* message's zone override; not copied into the logged marker); null for a person's message.
|
|
307
|
+
*/
|
|
308
|
+
schedule: ColumnType<(ScheduleMarker & {
|
|
309
|
+
timeZone?: string;
|
|
310
|
+
}) | null, string | null | undefined, string | null>;
|
|
300
311
|
created_at: Ts;
|
|
301
312
|
updated_at: Ts;
|
|
302
313
|
}
|
|
@@ -342,6 +353,11 @@ export interface RunsTable {
|
|
|
342
353
|
* and its descendants' tokens, agent map spec §6); null = the run is its own budget run.
|
|
343
354
|
*/
|
|
344
355
|
budget_run_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
356
|
+
/**
|
|
357
|
+
* 0113: the scheduled task run this run serves (set on the run the task's message triggers and
|
|
358
|
+
* inherited by its unattended descendants); null = a person's run.
|
|
359
|
+
*/
|
|
360
|
+
scheduled_task_run_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
345
361
|
}
|
|
346
362
|
/** 0053: one row per sub-agent dispatch (an `Agent` call of a parent run). */
|
|
347
363
|
export interface SubagentDispatchesTable {
|
|
@@ -488,7 +504,7 @@ export interface ApprovalsTable {
|
|
|
488
504
|
input: Json<unknown>;
|
|
489
505
|
requested_for: string | null;
|
|
490
506
|
approver_role: string | null;
|
|
491
|
-
status: Generated<"pending" | "approved" | "denied" | "expired" | "cancelled">;
|
|
507
|
+
status: Generated<"pending" | "approved" | "denied" | "expired" | "cancelled" | "skipped">;
|
|
492
508
|
scope: "once" | "session" | "always" | null;
|
|
493
509
|
decided_by: string | null;
|
|
494
510
|
decided_at: TsNullable;
|
|
@@ -509,6 +525,10 @@ export interface ApprovalsTable {
|
|
|
509
525
|
} | null, string | null | undefined, string | null>;
|
|
510
526
|
/** 0086: the scopes the person may choose (null: derived, see `approvals.service.ts`). */
|
|
511
527
|
scopes: ColumnType<("once" | "session" | "always")[] | null, ("once" | "session" | "always")[] | null | undefined, ("once" | "session" | "always")[] | null>;
|
|
528
|
+
/** 0114: what is asked: a tool call ('tool') or a refused network domain ('network'). */
|
|
529
|
+
kind: Generated<"tool" | "network">;
|
|
530
|
+
/** 0114: a network ask's hosts, command, kept result and outcome (null for tool approvals). */
|
|
531
|
+
detail: ColumnType<unknown | null, string | null | undefined, string | null>;
|
|
512
532
|
}
|
|
513
533
|
/** 0010_tools.sql:AskUserQuestion 提问。 */
|
|
514
534
|
export interface QuestionsTable {
|
|
@@ -628,6 +648,11 @@ export interface ChannelDeliveriesTable {
|
|
|
628
648
|
fail_seq: ColumnType<string | null, number | string | null | undefined, number | string | null>;
|
|
629
649
|
fail_count: Generated<number>;
|
|
630
650
|
updated_at: Ts;
|
|
651
|
+
/**
|
|
652
|
+
* 0113: a scheduled group run's thread is still to bind; the account its thread row gets when
|
|
653
|
+
* outbound has the run's first post (thread key = that message's id). Null otherwise.
|
|
654
|
+
*/
|
|
655
|
+
pending_thread_account_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
631
656
|
}
|
|
632
657
|
/** 0018_channels.sql:出站投递发出的审批 / 提问卡片对应的 IM 消息(Task 11,用于更新卡片状态)。 */
|
|
633
658
|
export interface ChannelCardMessagesTable {
|
|
@@ -850,6 +875,8 @@ export interface DevicesTable {
|
|
|
850
875
|
provider: ColumnType<string | null, string | null | undefined, string | null>;
|
|
851
876
|
/** 0096: the provider a demoted default was demoted from; null for every other device. */
|
|
852
877
|
demoted_from_provider: ColumnType<string | null, string | null | undefined, string | null>;
|
|
878
|
+
/** 0117: a cloud device's own sandbox tier; null follows the org's default. */
|
|
879
|
+
tier: ColumnType<SandboxTier | null, SandboxTier | null | undefined, SandboxTier | null>;
|
|
853
880
|
created_at: Ts;
|
|
854
881
|
updated_at: Ts;
|
|
855
882
|
deleted_at: TsNullable;
|
|
@@ -879,6 +906,12 @@ export interface DeviceInstancesTable {
|
|
|
879
906
|
prev_provider: ColumnType<string | null, string | null | undefined, string | null>;
|
|
880
907
|
prev_handle: InstanceHandle;
|
|
881
908
|
sweep_failed_at: TsNullable;
|
|
909
|
+
/**
|
|
910
|
+
* 0117: the limits in force on the instance, `{ tier, egress }`, plus `unapplied` when the
|
|
911
|
+
* provider could not apply a newer value without a file reset; null = unknown (made before
|
|
912
|
+
* 0117). Read with `readInstanceLimits` (`sandbox/tiers.ts`).
|
|
913
|
+
*/
|
|
914
|
+
instance_limits: ColumnType<unknown | null, string | null | undefined, string | null>;
|
|
882
915
|
}
|
|
883
916
|
/**
|
|
884
917
|
* 0093_connected_environments.sql, renamed in 0105: the machine of a local device. At most one
|
|
@@ -902,6 +935,10 @@ export interface DeviceMachinesTable {
|
|
|
902
935
|
last_seen_at: TsNullable;
|
|
903
936
|
created_at: Ts;
|
|
904
937
|
revoked_at: TsNullable;
|
|
938
|
+
/** 0114: the features the program reported in its hello (`networkReport`, `policyOp`). */
|
|
939
|
+
features: ColumnType<string[], string[] | undefined, string[]>;
|
|
940
|
+
/** 0114: the program's local network policy summary (hello, refreshed by each `policy` op). */
|
|
941
|
+
local_policy: ColumnType<unknown | null, string | null | undefined, string | null>;
|
|
905
942
|
}
|
|
906
943
|
/** 0088_environments.sql, renamed in 0105: the device a member's new conversations use by default. */
|
|
907
944
|
export interface DeviceDefaultsTable {
|
|
@@ -1147,6 +1184,61 @@ export interface ModelUsageTable {
|
|
|
1147
1184
|
/** Set when the row's `usage.report` job gave up after its last attempt (the sweep skips it). */
|
|
1148
1185
|
report_failed_at: TsNullable;
|
|
1149
1186
|
}
|
|
1187
|
+
type NullableText = ColumnType<string | null, string | null | undefined, string | null>;
|
|
1188
|
+
type NullableTs = ColumnType<Date | null, Date | string | null | undefined, Date | string | null>;
|
|
1189
|
+
/** 0113: a scheduled task (scheduled tasks spec §4). */
|
|
1190
|
+
export interface ScheduledTasksTable {
|
|
1191
|
+
id: string;
|
|
1192
|
+
org_id: string;
|
|
1193
|
+
account_id: string;
|
|
1194
|
+
agent_id: string;
|
|
1195
|
+
name: string;
|
|
1196
|
+
prompt: string;
|
|
1197
|
+
trigger: ColumnType<ScheduleTrigger, string, string>;
|
|
1198
|
+
mode: ScheduleMode;
|
|
1199
|
+
conversation_id: NullableText;
|
|
1200
|
+
chat: ColumnType<ScheduleChat | null, string | null | undefined, string | null>;
|
|
1201
|
+
model: ColumnType<ModelRef | null, string | null | undefined, string | null>;
|
|
1202
|
+
effort: NullableText;
|
|
1203
|
+
device_id: NullableText;
|
|
1204
|
+
status: ColumnType<ScheduleStatus, ScheduleStatus | undefined, ScheduleStatus>;
|
|
1205
|
+
pause_reason: ColumnType<SchedulePauseReason | null, SchedulePauseReason | null | undefined, SchedulePauseReason | null>;
|
|
1206
|
+
paused_by: NullableText;
|
|
1207
|
+
next_run_at: NullableTs;
|
|
1208
|
+
jitter_seconds: ColumnType<number, number | undefined, number>;
|
|
1209
|
+
expires_at: NullableTs;
|
|
1210
|
+
consecutive_failures: ColumnType<number, number | undefined, number>;
|
|
1211
|
+
created_at: Ts;
|
|
1212
|
+
updated_at: Ts;
|
|
1213
|
+
deleted_at: NullableTs;
|
|
1214
|
+
}
|
|
1215
|
+
/** 0113: one tick (or trial run) of a scheduled task; unique per (task, tick) when not trial. */
|
|
1216
|
+
export interface ScheduledTaskRunsTable {
|
|
1217
|
+
id: string;
|
|
1218
|
+
task_id: string;
|
|
1219
|
+
org_id: string;
|
|
1220
|
+
scheduled_for: Date | string;
|
|
1221
|
+
fired_at: NullableTs;
|
|
1222
|
+
trial: ColumnType<boolean, boolean | undefined, boolean>;
|
|
1223
|
+
/** A person's "run now" (trial or not): runs even while the task is paused. */
|
|
1224
|
+
manual: ColumnType<boolean, boolean | undefined, boolean>;
|
|
1225
|
+
conversation_id: NullableText;
|
|
1226
|
+
run_id: NullableText;
|
|
1227
|
+
status: ColumnType<TaskRunStatus, TaskRunStatus | undefined, TaskRunStatus>;
|
|
1228
|
+
skip_reason: ColumnType<TaskRunSkipReason | null, TaskRunSkipReason | null | undefined, TaskRunSkipReason | null>;
|
|
1229
|
+
error: NullableText;
|
|
1230
|
+
missing_grants: ColumnType<string[], string | undefined, string>;
|
|
1231
|
+
/** When the scanner's repair last found this started run still in flight. */
|
|
1232
|
+
rechecked_at: NullableTs;
|
|
1233
|
+
}
|
|
1234
|
+
/** 0113: a scheduled task's remembered grants (same shape as `conversation_tool_grants`). */
|
|
1235
|
+
export interface ScheduledTaskGrantsTable {
|
|
1236
|
+
task_id: string;
|
|
1237
|
+
tool_key: string;
|
|
1238
|
+
spec_hash: string;
|
|
1239
|
+
connector_id: string | null;
|
|
1240
|
+
created_at: Ts;
|
|
1241
|
+
}
|
|
1150
1242
|
export interface KernelDatabase {
|
|
1151
1243
|
accounts: AccountsTable;
|
|
1152
1244
|
account_initial_passwords: AccountInitialPasswordsTable;
|
|
@@ -1200,6 +1292,9 @@ export interface KernelDatabase {
|
|
|
1200
1292
|
device_defaults: DeviceDefaultsTable;
|
|
1201
1293
|
device_machines: DeviceMachinesTable;
|
|
1202
1294
|
background_tasks: BackgroundTasksTable;
|
|
1295
|
+
scheduled_tasks: ScheduledTasksTable;
|
|
1296
|
+
scheduled_task_runs: ScheduledTaskRunsTable;
|
|
1297
|
+
scheduled_task_grants: ScheduledTaskGrantsTable;
|
|
1203
1298
|
skills: SkillsTable;
|
|
1204
1299
|
skill_versions: SkillVersionsTable;
|
|
1205
1300
|
skill_change_requests: SkillChangeRequestsTable;
|
|
@@ -1669,6 +1764,8 @@ export interface PluginImportStagingTable {
|
|
|
1669
1764
|
expires_at: ColumnType<Date, Date | string, Date | string>;
|
|
1670
1765
|
}
|
|
1671
1766
|
type NullableCol<T> = ColumnType<T | null, T | null | undefined, T | null>;
|
|
1767
|
+
/** 0077 / 0085 / 0115: the kinds of a catalog request. */
|
|
1768
|
+
type CatalogRequestKindCol = "access" | "import" | "enable" | "connector_setup" | "network_allowlist";
|
|
1672
1769
|
/**
|
|
1673
1770
|
* 0037, renamed in 0042, kinds since 0077: an org's catalog requests. `stage` (org / deployment) is
|
|
1674
1771
|
* still written (`enable` -> org, else deployment) but no longer read.
|
|
@@ -1680,7 +1777,12 @@ export interface CatalogRequestsTable {
|
|
|
1680
1777
|
item_id: NullableCol<string>;
|
|
1681
1778
|
version: NullableCol<string>;
|
|
1682
1779
|
/** 0077: what the request asks for (spec §5.1). */
|
|
1683
|
-
kind: ColumnType<
|
|
1780
|
+
kind: ColumnType<CatalogRequestKindCol, CatalogRequestKindCol | undefined, CatalogRequestKindCol>;
|
|
1781
|
+
/**
|
|
1782
|
+
* 0115: what a `network_allowlist` request asks for (`{ hosts, conversationId, agentId }`); null
|
|
1783
|
+
* for other kinds.
|
|
1784
|
+
*/
|
|
1785
|
+
detail: ColumnType<unknown | null, string | null | undefined, string | null>;
|
|
1684
1786
|
/** 0085: the agent of a `connector_setup` request (cascades with the agent). */
|
|
1685
1787
|
agent_id: NullableCol<string>;
|
|
1686
1788
|
/** 0085: the template connector keys a `connector_setup` request still waits for. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ApiError } from "../errors.js";
|
|
2
2
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
3
3
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
4
|
+
import type { DeviceSandboxLevel } from "../runtime/context/device-paths.js";
|
|
4
5
|
import { SandboxProviders } from "../sandbox/providers.js";
|
|
5
6
|
import { DeviceAccess } from "./access.js";
|
|
6
7
|
import { DevicesService } from "./devices.service.js";
|
|
@@ -29,7 +30,13 @@ export declare class ConversationDevices {
|
|
|
29
30
|
private readonly providers;
|
|
30
31
|
private readonly access;
|
|
31
32
|
private readonly devices;
|
|
32
|
-
constructor(db: KernelDb, txHost: TransactionHost, providers: Pick<SandboxProviders, "cloud"> & Partial<Pick<SandboxProviders, "forName">>, access: Pick<DeviceAccess, "usable">, devices: Pick<DevicesService, "ensureDefault">);
|
|
33
|
+
constructor(db: KernelDb, txHost: TransactionHost, providers: Pick<SandboxProviders, "cloud"> & Partial<Pick<SandboxProviders, "forName">>, access: Pick<DeviceAccess, "usable">, devices: Pick<DevicesService, "ensureDefault" | "sandboxLevel">);
|
|
34
|
+
/**
|
|
35
|
+
* The sandbox level a call in the device runs at now ({@link DevicesService.sandboxLevel}),
|
|
36
|
+
* recorded for the model in `device_paths` (sandbox dev tiers spec §11.5); `runId`: the calling
|
|
37
|
+
* run, whose scheduled-run cap applies.
|
|
38
|
+
*/
|
|
39
|
+
sandboxLevel(orgId: string, deviceId: string, runId?: string): Promise<DeviceSandboxLevel | null>;
|
|
33
40
|
/**
|
|
34
41
|
* The device a new conversation of `accountId` is bound to: the chosen one when it is
|
|
35
42
|
* usable by the person (else `device.not_found` / `device.unavailable`, and nothing
|
|
@@ -41,6 +41,14 @@ let ConversationDevices = class ConversationDevices {
|
|
|
41
41
|
this.access = access;
|
|
42
42
|
this.devices = devices;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The sandbox level a call in the device runs at now ({@link DevicesService.sandboxLevel}),
|
|
46
|
+
* recorded for the model in `device_paths` (sandbox dev tiers spec §11.5); `runId`: the calling
|
|
47
|
+
* run, whose scheduled-run cap applies.
|
|
48
|
+
*/
|
|
49
|
+
sandboxLevel(orgId, deviceId, runId) {
|
|
50
|
+
return this.devices.sandboxLevel(orgId, deviceId, runId);
|
|
51
|
+
}
|
|
44
52
|
/**
|
|
45
53
|
* The device a new conversation of `accountId` is bound to: the chosen one when it is
|
|
46
54
|
* usable by the person (else `device.not_found` / `device.unavailable`, and nothing
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { DeviceLocalPolicySummary } from "@capybara-hq/plugin-sdk";
|
|
1
2
|
import type { z } from "zod";
|
|
2
3
|
import { type Principal } from "../identity/principal.js";
|
|
3
4
|
import { type OrgContext } from "../orgs/org.guard.js";
|
|
4
5
|
import { type DeviceList, DevicesService, type DeviceView } from "./devices.service.js";
|
|
5
|
-
import { type DeviceNameBody, type DeviceParams } from "./dto.js";
|
|
6
|
+
import { type DeviceNameBody, type DeviceParams, type DevicePatchBody, type DevicePolicyBody } from "./dto.js";
|
|
6
7
|
/**
|
|
7
8
|
* A person's devices in an org (`/api/v1/orgs/:orgId/devices`, spec
|
|
8
9
|
* 2026-11-07-environments-design §9; `agents.use`, human users only: a service token gets 403).
|
|
@@ -10,12 +11,25 @@ import { type DeviceNameBody, type DeviceParams } from "./dto.js";
|
|
|
10
11
|
* gets 404 `device.not_found`.
|
|
11
12
|
* - `GET /` → `{ items, defaultId, lastUsedId, cloudAvailable }` (`cloudAvailable`: there is a
|
|
12
13
|
* current sandbox provider; ruling R4);
|
|
13
|
-
* - `POST / { name }` → 201 `{ device }`; `GET /:id
|
|
14
|
+
* - `POST / { name }` → 201 `{ device }`; `GET /:id` → `{ device }`;
|
|
15
|
+
* - `PATCH /:id { name?, tier? }` → `{ device }`: renames it and, for a cloud device, sets its
|
|
16
|
+
* sandbox tier (sandbox dev tiers spec §7.2; null follows the org's default). The tier is set in
|
|
17
|
+
* a web session only (an API token → 403 `auth.session_required`); above the org's or the
|
|
18
|
+
* platform's ceiling → 400 `device.tier_not_allowed`; a local device → 409
|
|
19
|
+
* `device.tier_on_device` (its tier is its machine's: `POST /:id/policy { tier }`). Audited
|
|
20
|
+
* `device.tier_changed`;
|
|
14
21
|
* - `DELETE /:id` → 202 (destroyed by a job after the response);
|
|
15
22
|
* - `POST /:id/files/reset` → 202 (the reset is enqueued: a worker empties the files, the next use
|
|
16
23
|
* starts empty);
|
|
17
24
|
* - `DELETE /:id/machine` → 204: a local device's machine is disconnected (its credential
|
|
18
|
-
* revoked; it must sign in again)
|
|
25
|
+
* revoked; it must sign in again);
|
|
26
|
+
* - `POST /:id/policy { add?, remove?, tier?, isolation? }` → `{ policy }`: the owner changes the
|
|
27
|
+
* local device's policy on its machine (network ask-to-allow spec §7, sandbox dev tiers spec
|
|
28
|
+
* §7.3; `isolation` is only ever `sandbox`). Web session only
|
|
29
|
+
* (`@SessionOnly`: a personal access token → 403 `auth.session_required`, a service token → 403):
|
|
30
|
+
* a person changes their own machine's network policy, nothing model-driven does. Widening needs
|
|
31
|
+
* the machine's `remoteGrants: "owner"` (409 `device.policy_refused`); offline → 503
|
|
32
|
+
* `device.offline`; cloud or an older program → 409 `device.policy_unsupported`.
|
|
19
33
|
*/
|
|
20
34
|
export declare class DevicesController {
|
|
21
35
|
private readonly devices;
|
|
@@ -27,10 +41,13 @@ export declare class DevicesController {
|
|
|
27
41
|
get(principal: Principal, org: OrgContext, params: z.infer<typeof DeviceParams>): Promise<{
|
|
28
42
|
device: DeviceView;
|
|
29
43
|
}>;
|
|
30
|
-
|
|
44
|
+
update(principal: Principal, org: OrgContext, params: z.infer<typeof DeviceParams>, body: z.infer<typeof DevicePatchBody>): Promise<{
|
|
31
45
|
device: DeviceView;
|
|
32
46
|
}>;
|
|
33
47
|
remove(principal: Principal, org: OrgContext, params: z.infer<typeof DeviceParams>): Promise<void>;
|
|
34
48
|
resetFiles(principal: Principal, org: OrgContext, params: z.infer<typeof DeviceParams>): Promise<void>;
|
|
35
49
|
revokeMachine(principal: Principal, org: OrgContext, params: z.infer<typeof DeviceParams>): Promise<void>;
|
|
50
|
+
changePolicy(principal: Principal, org: OrgContext, params: z.infer<typeof DeviceParams>, body: z.infer<typeof DevicePolicyBody>): Promise<{
|
|
51
|
+
policy: DeviceLocalPolicySummary;
|
|
52
|
+
}>;
|
|
36
53
|
}
|
|
@@ -8,11 +8,12 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { Controller, Delete, Get, HttpCode, Inject, Patch, Post } from "@nestjs/common";
|
|
11
|
-
import {
|
|
11
|
+
import { ApiError } from "../errors.js";
|
|
12
|
+
import { CurrentPrincipal, patTokenId, requireUser, SessionOnly, } from "../identity/principal.js";
|
|
12
13
|
import { ZBody, ZParam } from "../infra/validation/zod-params.js";
|
|
13
14
|
import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
|
|
14
15
|
import { DevicesService } from "./devices.service.js";
|
|
15
|
-
import { DeviceCreateBodyDto, DeviceParamsDto,
|
|
16
|
+
import { DeviceCreateBodyDto, DeviceParamsDto, DevicePatchBodyDto, DevicePolicyBodyDto, } from "./dto.js";
|
|
16
17
|
/**
|
|
17
18
|
* A person's devices in an org (`/api/v1/orgs/:orgId/devices`, spec
|
|
18
19
|
* 2026-11-07-environments-design §9; `agents.use`, human users only: a service token gets 403).
|
|
@@ -20,12 +21,25 @@ import { DeviceCreateBodyDto, DeviceParamsDto, DeviceRenameBodyDto, } from "./dt
|
|
|
20
21
|
* gets 404 `device.not_found`.
|
|
21
22
|
* - `GET /` → `{ items, defaultId, lastUsedId, cloudAvailable }` (`cloudAvailable`: there is a
|
|
22
23
|
* current sandbox provider; ruling R4);
|
|
23
|
-
* - `POST / { name }` → 201 `{ device }`; `GET /:id
|
|
24
|
+
* - `POST / { name }` → 201 `{ device }`; `GET /:id` → `{ device }`;
|
|
25
|
+
* - `PATCH /:id { name?, tier? }` → `{ device }`: renames it and, for a cloud device, sets its
|
|
26
|
+
* sandbox tier (sandbox dev tiers spec §7.2; null follows the org's default). The tier is set in
|
|
27
|
+
* a web session only (an API token → 403 `auth.session_required`); above the org's or the
|
|
28
|
+
* platform's ceiling → 400 `device.tier_not_allowed`; a local device → 409
|
|
29
|
+
* `device.tier_on_device` (its tier is its machine's: `POST /:id/policy { tier }`). Audited
|
|
30
|
+
* `device.tier_changed`;
|
|
24
31
|
* - `DELETE /:id` → 202 (destroyed by a job after the response);
|
|
25
32
|
* - `POST /:id/files/reset` → 202 (the reset is enqueued: a worker empties the files, the next use
|
|
26
33
|
* starts empty);
|
|
27
34
|
* - `DELETE /:id/machine` → 204: a local device's machine is disconnected (its credential
|
|
28
|
-
* revoked; it must sign in again)
|
|
35
|
+
* revoked; it must sign in again);
|
|
36
|
+
* - `POST /:id/policy { add?, remove?, tier?, isolation? }` → `{ policy }`: the owner changes the
|
|
37
|
+
* local device's policy on its machine (network ask-to-allow spec §7, sandbox dev tiers spec
|
|
38
|
+
* §7.3; `isolation` is only ever `sandbox`). Web session only
|
|
39
|
+
* (`@SessionOnly`: a personal access token → 403 `auth.session_required`, a service token → 403):
|
|
40
|
+
* a person changes their own machine's network policy, nothing model-driven does. Widening needs
|
|
41
|
+
* the machine's `remoteGrants: "owner"` (409 `device.policy_refused`); offline → 503
|
|
42
|
+
* `device.offline`; cloud or an older program → 409 `device.policy_unsupported`.
|
|
29
43
|
*/
|
|
30
44
|
let DevicesController = class DevicesController {
|
|
31
45
|
devices;
|
|
@@ -46,10 +60,18 @@ let DevicesController = class DevicesController {
|
|
|
46
60
|
const p = requireUser(principal);
|
|
47
61
|
return { device: await this.devices.get(org.orgId, p.account.id, params.id) };
|
|
48
62
|
}
|
|
49
|
-
async
|
|
63
|
+
async update(principal, org, params, body) {
|
|
50
64
|
const p = requireUser(principal);
|
|
65
|
+
const tokenId = patTokenId(p);
|
|
66
|
+
// a person decides their device's sandbox level, in a session (spec §7.2)
|
|
67
|
+
if (body.tier !== undefined && tokenId !== undefined) {
|
|
68
|
+
throw new ApiError("auth.session_required", "A device's sandbox level is set by its owner on the web, not with an API token");
|
|
69
|
+
}
|
|
51
70
|
return {
|
|
52
|
-
device: await this.devices.
|
|
71
|
+
device: await this.devices.update(org.orgId, p.account.id, params.id, {
|
|
72
|
+
...(body.name !== undefined && { name: body.name }),
|
|
73
|
+
...(body.tier !== undefined && { tier: body.tier }),
|
|
74
|
+
}, tokenId),
|
|
53
75
|
};
|
|
54
76
|
}
|
|
55
77
|
async remove(principal, org, params) {
|
|
@@ -64,6 +86,12 @@ let DevicesController = class DevicesController {
|
|
|
64
86
|
const p = requireUser(principal);
|
|
65
87
|
await this.devices.revokeMachine(org.orgId, p.account.id, params.id, patTokenId(p));
|
|
66
88
|
}
|
|
89
|
+
async changePolicy(principal, org, params, body) {
|
|
90
|
+
const p = requireUser(principal);
|
|
91
|
+
return {
|
|
92
|
+
policy: await this.devices.changePolicy(org.orgId, p.account, params.id, body),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
67
95
|
};
|
|
68
96
|
__decorate([
|
|
69
97
|
Get(),
|
|
@@ -91,8 +119,8 @@ __decorate([
|
|
|
91
119
|
__param(0, CurrentPrincipal()),
|
|
92
120
|
__param(1, CurrentOrg()),
|
|
93
121
|
__param(2, ZParam(DeviceParamsDto)),
|
|
94
|
-
__param(3, ZBody(
|
|
95
|
-
], DevicesController.prototype, "
|
|
122
|
+
__param(3, ZBody(DevicePatchBodyDto))
|
|
123
|
+
], DevicesController.prototype, "update", null);
|
|
96
124
|
__decorate([
|
|
97
125
|
Delete(":id"),
|
|
98
126
|
HttpCode(202),
|
|
@@ -117,6 +145,16 @@ __decorate([
|
|
|
117
145
|
__param(1, CurrentOrg()),
|
|
118
146
|
__param(2, ZParam(DeviceParamsDto))
|
|
119
147
|
], DevicesController.prototype, "revokeMachine", null);
|
|
148
|
+
__decorate([
|
|
149
|
+
Post(":id/policy"),
|
|
150
|
+
HttpCode(200),
|
|
151
|
+
SessionOnly(),
|
|
152
|
+
OrgAction("agents.use"),
|
|
153
|
+
__param(0, CurrentPrincipal()),
|
|
154
|
+
__param(1, CurrentOrg()),
|
|
155
|
+
__param(2, ZParam(DeviceParamsDto)),
|
|
156
|
+
__param(3, ZBody(DevicePolicyBodyDto))
|
|
157
|
+
], DevicesController.prototype, "changePolicy", null);
|
|
120
158
|
DevicesController = __decorate([
|
|
121
159
|
Controller("api/v1/orgs/:orgId/devices"),
|
|
122
160
|
__param(0, Inject(DevicesService))
|
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import type { RouteSchemas } from "../platform/route-schemas.js";
|
|
2
|
-
/** The
|
|
2
|
+
/** The body of the excluded `POST /devices/:id/policy` route (human-only). */
|
|
3
|
+
export { DevicePolicyBody } from "./dto.js";
|
|
4
|
+
/**
|
|
5
|
+
* The devices routes the Platform operations mirror (`check:platform-ops` keys). Changing a
|
|
6
|
+
* device's network policy (`POST /devices/:id/policy`, body {@link DevicePolicyBody}) is a person's
|
|
7
|
+
* act on their own machine and stays excluded, so it has no entry here. `PATCH /devices/:id` takes
|
|
8
|
+
* a cloud device's `tier` too ({@link DevicePatchBody}); `devices.rename` omits it: a person decides
|
|
9
|
+
* their device's sandbox level.
|
|
10
|
+
*/
|
|
3
11
|
export declare const DEVICE_ROUTES: readonly RouteSchemas[];
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import { DeviceNameBody, DeviceParams } from "./dto.js";
|
|
2
|
-
/** The
|
|
1
|
+
import { DeviceNameBody, DeviceParams, DevicePatchBody } from "./dto.js";
|
|
2
|
+
/** The body of the excluded `POST /devices/:id/policy` route (human-only). */
|
|
3
|
+
export { DevicePolicyBody } from "./dto.js";
|
|
4
|
+
/**
|
|
5
|
+
* The devices routes the Platform operations mirror (`check:platform-ops` keys). Changing a
|
|
6
|
+
* device's network policy (`POST /devices/:id/policy`, body {@link DevicePolicyBody}) is a person's
|
|
7
|
+
* act on their own machine and stays excluded, so it has no entry here. `PATCH /devices/:id` takes
|
|
8
|
+
* a cloud device's `tier` too ({@link DevicePatchBody}); `devices.rename` omits it: a person decides
|
|
9
|
+
* their device's sandbox level.
|
|
10
|
+
*/
|
|
3
11
|
export const DEVICE_ROUTES = [
|
|
4
12
|
{ route: "GET /api/v1/orgs/:orgId/devices" },
|
|
5
13
|
{ route: "POST /api/v1/orgs/:orgId/devices", body: DeviceNameBody },
|
|
@@ -7,7 +15,7 @@ export const DEVICE_ROUTES = [
|
|
|
7
15
|
{
|
|
8
16
|
route: "PATCH /api/v1/orgs/:orgId/devices/:id",
|
|
9
17
|
params: DeviceParams,
|
|
10
|
-
body:
|
|
18
|
+
body: DevicePatchBody,
|
|
11
19
|
},
|
|
12
20
|
{ route: "DELETE /api/v1/orgs/:orgId/devices/:id", params: DeviceParams },
|
|
13
21
|
{ route: "POST /api/v1/orgs/:orgId/devices/:id/files/reset", params: DeviceParams },
|