@capybara-hq/kernel 0.1.0-rc.27 → 0.1.0-rc.28
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/dist/app.js +2 -0
- package/dist/catalog/catalog-content.d.ts +15 -3
- package/dist/catalog/catalog-content.js +5 -3
- package/dist/catalog/catalog-scope.service.js +3 -2
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-catalog.service.js +10 -3
- package/dist/catalog/plugin-install.d.ts +34 -18
- package/dist/catalog/plugin-install.js +84 -46
- package/dist/catalog/plugin-upgrade.js +1 -0
- package/dist/channels/actions.d.ts +4 -1
- package/dist/channels/actions.js +5 -2
- package/dist/channels/cards.d.ts +7 -1
- package/dist/channels/cards.js +28 -1
- package/dist/channels/phrases.d.ts +15 -0
- package/dist/channels/phrases.js +9 -0
- package/dist/config/schema.d.ts +8 -0
- package/dist/config/schema.js +69 -0
- package/dist/conversations/events.d.ts +14 -0
- package/dist/conversations/events.js +20 -0
- package/dist/db/kernel-schema.d.ts +19 -4
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +5 -0
- package/dist/git/gateway.d.ts +93 -0
- package/dist/git/gateway.js +750 -0
- package/dist/git/gateway.route.d.ts +16 -0
- package/dist/git/gateway.route.js +41 -0
- package/dist/git/git.module.d.ts +10 -0
- package/dist/git/git.module.js +31 -0
- package/dist/git/identity.d.ts +80 -0
- package/dist/git/identity.js +209 -0
- package/dist/git/pkt-line.d.ts +53 -0
- package/dist/git/pkt-line.js +146 -0
- package/dist/git/run-end.d.ts +29 -0
- package/dist/git/run-end.js +121 -0
- package/dist/git/sources.d.ts +42 -0
- package/dist/git/sources.js +134 -0
- package/dist/git/tickets.d.ts +69 -0
- package/dist/git/tickets.js +194 -0
- package/dist/http/api.module.js +3 -0
- package/dist/http/app.d.ts +6 -0
- package/dist/http/app.js +3 -0
- package/dist/infra/config/kernel-config.service.js +2 -0
- package/dist/orgs/settings.service.d.ts +21 -0
- package/dist/orgs/settings.service.js +18 -0
- package/dist/overrides.d.ts +6 -0
- package/dist/overrides.js +4 -0
- package/dist/platform/exclusions.js +3 -0
- package/dist/runtime/run-finish.d.ts +5 -0
- package/dist/runtime/run-finish.js +12 -3
- package/dist/runtime/runtime.module.js +2 -0
- package/dist/runtime/waits.js +7 -5
- package/dist/schedules/fire.d.ts +3 -3
- package/dist/schedules/fire.js +7 -4
- package/dist/schedules/grant-rules.d.ts +40 -0
- package/dist/schedules/grant-rules.js +83 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +9 -7
- package/dist/schedules/scheduled-tasks.service.js +50 -15
- package/dist/schedules/types.d.ts +2 -0
- package/dist/tools/approvals.service.d.ts +100 -19
- package/dist/tools/approvals.service.js +288 -21
- package/dist/tools/builtin-names.d.ts +4 -2
- package/dist/tools/builtin-names.js +6 -1
- package/dist/tools/builtins/schedules.d.ts +11 -9
- package/dist/tools/builtins/schedules.js +202 -44
- package/dist/tools/connector-account-policies.d.ts +8 -0
- package/dist/tools/connector-account-policies.js +8 -0
- package/dist/tools/connectors.controller.d.ts +2 -0
- package/dist/tools/connectors.controller.js +5 -1
- package/dist/tools/connectors.service.d.ts +46 -3
- package/dist/tools/connectors.service.js +98 -5
- package/dist/tools/credentials.d.ts +18 -0
- package/dist/tools/credentials.js +60 -0
- package/dist/tools/credentials.module.d.ts +7 -0
- package/dist/tools/credentials.module.js +22 -0
- package/dist/tools/git-asks.d.ts +113 -0
- package/dist/tools/git-asks.js +174 -0
- package/dist/tools/git-push-policy.d.ts +89 -0
- package/dist/tools/git-push-policy.js +175 -0
- package/dist/tools/info.d.ts +3 -3
- package/dist/tools/info.js +3 -3
- package/dist/tools/network-asks.d.ts +5 -0
- package/dist/tools/network-asks.js +9 -2
- package/dist/tools/tools.module.js +4 -3
- package/package.json +3 -3
- package/src/migrations/0126_git_gateway.sql +10 -0
- package/src/migrations/0127_git_push_policy.sql +12 -0
- package/src/migrations/0128_scheduled_task_plugin_disabled.sql +10 -0
- package/src/migrations/0129_connector_plugin_disabled.sql +5 -0
|
@@ -29,6 +29,8 @@ import { OrgLock } from "./org-lock.js";
|
|
|
29
29
|
* absent (or an invalid stored value) = follow the platform, personal picks allowed.
|
|
30
30
|
* - `uiArrangement`: per slot, the extension contributions the org hides and their order (UI slots
|
|
31
31
|
* spec §5); a slot with no entry follows the platform's; an invalid stored value reads as absent.
|
|
32
|
+
* - `gitGateway`: the org's git gateway switch and host sources (sandbox git credentials spec §9);
|
|
33
|
+
* not in the PATCH yet; an invalid stored value reads as `{ enabled: false }` (fails closed).
|
|
32
34
|
*/
|
|
33
35
|
/** 组织沙箱出站白名单的主机数上限。 */
|
|
34
36
|
export declare const ORG_SANDBOX_ALLOW_HOSTS_MAX = 200;
|
|
@@ -117,6 +119,21 @@ export declare const ScheduledTasksPatchSchema: z.ZodObject<{
|
|
|
117
119
|
pauseAfterFailures: z.ZodOptional<z.ZodNumber>;
|
|
118
120
|
forbiddenTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
119
121
|
}, z.core.$strict>;
|
|
122
|
+
/**
|
|
123
|
+
* The org's git gateway setting (sandbox git credentials spec §9): `enabled: false` switches the
|
|
124
|
+
* gateway off for the org (absent = on); `hostSources` names, per git host, the connector whose
|
|
125
|
+
* credential the gateway uses there. Reads fail closed: an invalid stored value (not an object, or
|
|
126
|
+
* a field of the wrong type) reads as {@link GIT_GATEWAY_INVALID} (off), never wider than stored.
|
|
127
|
+
*/
|
|
128
|
+
export declare const GitGatewaySettingSchema: z.ZodObject<{
|
|
129
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
130
|
+
hostSources: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
131
|
+
}, z.core.$strip>;
|
|
132
|
+
/** What an invalid stored `gitGateway` reads as: the gateway off for the org. */
|
|
133
|
+
export declare const GIT_GATEWAY_INVALID: {
|
|
134
|
+
readonly enabled: false;
|
|
135
|
+
};
|
|
136
|
+
export type GitGatewaySetting = z.infer<typeof GitGatewaySettingSchema>;
|
|
120
137
|
export declare const OrgSettingsSchema: z.ZodObject<{
|
|
121
138
|
toolSearch: z.ZodCatch<z.ZodDefault<z.ZodEnum<{
|
|
122
139
|
off: "off";
|
|
@@ -165,6 +182,10 @@ export declare const OrgSettingsSchema: z.ZodObject<{
|
|
|
165
182
|
}>;
|
|
166
183
|
}, z.core.$strict>>>;
|
|
167
184
|
uiArrangement: z.ZodCatch<z.ZodOptional<z.ZodType<Partial<Record<"agent.detail.section" | "conversation.composer.frame" | "conversation.empty" | "conversation.panel" | "conversation.properties.section" | "conversation.tool-result.card" | "file.preview" | "org.page" | "org.settings.section" | "platform.page" | "shell.brand" | "shell.overlay", import("../ui/arrangement.js").UiArrangementEntry>>, unknown, z.core.$ZodTypeInternals<Partial<Record<"agent.detail.section" | "conversation.composer.frame" | "conversation.empty" | "conversation.panel" | "conversation.properties.section" | "conversation.tool-result.card" | "file.preview" | "org.page" | "org.settings.section" | "platform.page" | "shell.brand" | "shell.overlay", import("../ui/arrangement.js").UiArrangementEntry>>, unknown>>>>;
|
|
185
|
+
gitGateway: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
186
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
187
|
+
hostSources: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
188
|
+
}, z.core.$strip>>>;
|
|
168
189
|
scheduledTasks: z.ZodCatch<z.ZodPrefault<z.ZodObject<{
|
|
169
190
|
enabled: z.ZodCatch<z.ZodDefault<z.ZodBoolean>>;
|
|
170
191
|
minIntervalMinutes: z.ZodCatch<z.ZodDefault<z.ZodNumber>>;
|
|
@@ -45,6 +45,8 @@ import { OrgLock, OrgLockModule } from "./org-lock.js";
|
|
|
45
45
|
* absent (or an invalid stored value) = follow the platform, personal picks allowed.
|
|
46
46
|
* - `uiArrangement`: per slot, the extension contributions the org hides and their order (UI slots
|
|
47
47
|
* spec §5); a slot with no entry follows the platform's; an invalid stored value reads as absent.
|
|
48
|
+
* - `gitGateway`: the org's git gateway switch and host sources (sandbox git credentials spec §9);
|
|
49
|
+
* not in the PATCH yet; an invalid stored value reads as `{ enabled: false }` (fails closed).
|
|
48
50
|
*/
|
|
49
51
|
/** 组织沙箱出站白名单的主机数上限。 */
|
|
50
52
|
export const ORG_SANDBOX_ALLOW_HOSTS_MAX = 200;
|
|
@@ -139,6 +141,18 @@ export const ScheduledTasksPatchSchema = z
|
|
|
139
141
|
})
|
|
140
142
|
.partial()
|
|
141
143
|
.strict();
|
|
144
|
+
/**
|
|
145
|
+
* The org's git gateway setting (sandbox git credentials spec §9): `enabled: false` switches the
|
|
146
|
+
* gateway off for the org (absent = on); `hostSources` names, per git host, the connector whose
|
|
147
|
+
* credential the gateway uses there. Reads fail closed: an invalid stored value (not an object, or
|
|
148
|
+
* a field of the wrong type) reads as {@link GIT_GATEWAY_INVALID} (off), never wider than stored.
|
|
149
|
+
*/
|
|
150
|
+
export const GitGatewaySettingSchema = z.object({
|
|
151
|
+
enabled: z.boolean().optional(),
|
|
152
|
+
hostSources: z.record(z.string(), z.string()).optional(),
|
|
153
|
+
});
|
|
154
|
+
/** What an invalid stored `gitGateway` reads as: the gateway off for the org. */
|
|
155
|
+
export const GIT_GATEWAY_INVALID = { enabled: false };
|
|
142
156
|
export const OrgSettingsSchema = z.object({
|
|
143
157
|
toolSearch: z.enum(["on", "off"]).default("on").catch("on"),
|
|
144
158
|
locale: z.enum(["zh", "en"]).default("en").catch("en"),
|
|
@@ -155,6 +169,8 @@ export const OrgSettingsSchema = z.object({
|
|
|
155
169
|
theme: OrgThemeSettingSchema.optional().catch(undefined),
|
|
156
170
|
/** Hidden and ordered extension contributions per slot; absent = follow the platform. */
|
|
157
171
|
uiArrangement: UiArrangementSchema.optional().catch(undefined),
|
|
172
|
+
/** The org's git gateway switch and host sources; absent = on, sources chosen by default. */
|
|
173
|
+
gitGateway: GitGatewaySettingSchema.optional().catch({ ...GIT_GATEWAY_INVALID }),
|
|
158
174
|
/** Scheduled tasks (spec §9); the platform bounds clamp it, see `effectiveScheduleSettings`. */
|
|
159
175
|
scheduledTasks: ScheduledTasksSettingsSchema.prefault({}).catch(ScheduledTasksSettingsSchema.parse({})),
|
|
160
176
|
});
|
|
@@ -434,6 +450,8 @@ let OrgSettingsService = class OrgSettingsService {
|
|
|
434
450
|
delete after.theme;
|
|
435
451
|
if (after.uiArrangement === undefined)
|
|
436
452
|
delete after.uiArrangement;
|
|
453
|
+
if (after.gitGateway === undefined)
|
|
454
|
+
delete after.gitGateway;
|
|
437
455
|
if (sameSettings(before, after))
|
|
438
456
|
return before;
|
|
439
457
|
// Lock order: the org row (orgLock above), then the theme's catalog item and its row FOR
|
package/dist/overrides.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { EVAL_REPLAY_HOOKS } from "./evals/worker.js";
|
|
|
8
8
|
import { BOOTSTRAP_LOCK_TTL } from "./extensions/bootstrap.js";
|
|
9
9
|
import { USAGE_SUBSCRIBERS_CLOCK } from "./extensions/usage-subscribers.js";
|
|
10
10
|
import { FILE_PREVIEW_TIMEOUT } from "./files/previewers.js";
|
|
11
|
+
import { GIT_UPSTREAM_ORIGIN, type GitUpstreamOrigin } from "./git/gateway.js";
|
|
12
|
+
import { GIT_IDENTITY_API_BASE, type GitIdentityApiBase } from "./git/identity.js";
|
|
11
13
|
import { OUTBOX_DISPATCH_FAILS } from "./infra/outbox/outbox.dispatcher.js";
|
|
12
14
|
import { OUTBOX_FAST_PATH_SETTLED } from "./infra/outbox/outbox.module.js";
|
|
13
15
|
import { OUTBOX_STRICT_CONFLICTS } from "./infra/outbox/outbox.service.js";
|
|
@@ -40,6 +42,8 @@ import { EMBED_TOKEN_CLOCK } from "./ui/embed-token.js";
|
|
|
40
42
|
* - `EMBED_TOKEN_CLOCK`: the clock embed tokens are issued and checked with;
|
|
41
43
|
* - `RUN_CONTEXT_CLOCK`: the clock run start reads the run context's date from, and the
|
|
42
44
|
* `CurrentTime` builtin the time;
|
|
45
|
+
* - `GIT_IDENTITY_API_BASE`: the API base of a git host for the commit identity (a loopback fake);
|
|
46
|
+
* - `GIT_UPSTREAM_ORIGIN`: the origin the git gateway relays a git host to (a loopback fake);
|
|
43
47
|
* - `OUTBOX_STRICT_CONFLICTS`: a reused outbox job id with different data throws (tests) instead
|
|
44
48
|
* of logging an error;
|
|
45
49
|
* - `OUTBOX_DISPATCH_FAILS`: returns true to make the outbox dispatcher's adds to the given queue
|
|
@@ -69,6 +73,8 @@ interface KernelSeams {
|
|
|
69
73
|
[ADMIN_USAGE_TIMINGS]: Partial<AdminUsageTimings>;
|
|
70
74
|
[EMBED_TOKEN_CLOCK]: () => number;
|
|
71
75
|
[RUN_CONTEXT_CLOCK]: () => number;
|
|
76
|
+
[GIT_IDENTITY_API_BASE]: GitIdentityApiBase;
|
|
77
|
+
[GIT_UPSTREAM_ORIGIN]: GitUpstreamOrigin;
|
|
72
78
|
[OUTBOX_STRICT_CONFLICTS]: boolean;
|
|
73
79
|
[OUTBOX_DISPATCH_FAILS]: (queue: KernelQueueName) => boolean;
|
|
74
80
|
[USAGE_SUBSCRIBERS_CLOCK]: () => number;
|
package/dist/overrides.js
CHANGED
|
@@ -6,6 +6,8 @@ import { EVAL_REPLAY_HOOKS } from "./evals/worker.js";
|
|
|
6
6
|
import { BOOTSTRAP_LOCK_TTL } from "./extensions/bootstrap.js";
|
|
7
7
|
import { USAGE_SUBSCRIBERS_CLOCK } from "./extensions/usage-subscribers.js";
|
|
8
8
|
import { FILE_PREVIEW_TIMEOUT } from "./files/previewers.js";
|
|
9
|
+
import { GIT_UPSTREAM_ORIGIN } from "./git/gateway.js";
|
|
10
|
+
import { GIT_IDENTITY_API_BASE } from "./git/identity.js";
|
|
9
11
|
import { OUTBOX_DISPATCH_FAILS } from "./infra/outbox/outbox.dispatcher.js";
|
|
10
12
|
import { OUTBOX_FAST_PATH_SETTLED } from "./infra/outbox/outbox.module.js";
|
|
11
13
|
import { OUTBOX_STRICT_CONFLICTS } from "./infra/outbox/outbox.service.js";
|
|
@@ -38,6 +40,8 @@ const SEAM_TOKENS = [
|
|
|
38
40
|
ADMIN_USAGE_TIMINGS,
|
|
39
41
|
EMBED_TOKEN_CLOCK,
|
|
40
42
|
RUN_CONTEXT_CLOCK,
|
|
43
|
+
GIT_IDENTITY_API_BASE,
|
|
44
|
+
GIT_UPSTREAM_ORIGIN,
|
|
41
45
|
OUTBOX_STRICT_CONFLICTS,
|
|
42
46
|
OUTBOX_DISPATCH_FAILS,
|
|
43
47
|
OUTBOX_FAST_PATH_SETTLED,
|
|
@@ -282,6 +282,9 @@ export const PLATFORM_EXCLUSIONS = [
|
|
|
282
282
|
category: "transport",
|
|
283
283
|
reason: "Serves sandbox file bytes",
|
|
284
284
|
},
|
|
285
|
+
// git gateway (sandbox git credentials spec §5): `GET` / `POST /git/v1/*` is git smart-HTTP for
|
|
286
|
+
// sandboxes, authenticated by run tickets, not an API; it is a raw api-role Fastify route
|
|
287
|
+
// (`git/gateway.route.ts`), not a Nest controller, so it is not listed (like the webhook)
|
|
285
288
|
// devices (personal cloud devices, environments design phase 1)
|
|
286
289
|
// connected machines (environments design phase 2): device sign-in by the program and on the
|
|
287
290
|
// confirmation page, and disconnecting a machine. The machine's WebSocket (`GET /device/connect`)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AppendEvent, EventLog } from "../conversations/event-log.js";
|
|
2
|
+
import { GitRunEnd } from "../git/run-end.js";
|
|
2
3
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
3
4
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
4
5
|
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
@@ -30,6 +31,8 @@ export declare class RunFinish {
|
|
|
30
31
|
readonly txHost: TransactionHost;
|
|
31
32
|
/** A scheduled task's run follows its kernel run's end in the same transaction (spec §6). */
|
|
32
33
|
private readonly taskRuns;
|
|
34
|
+
/** The run's git tickets are revoked and its fetch summaries written after the commit. */
|
|
35
|
+
private readonly gitRunEnd;
|
|
33
36
|
/** The run's end owes the conversation's ensure-run backstop (transactional outbox spec §6). */
|
|
34
37
|
private readonly outbox;
|
|
35
38
|
/** A finished run's pending approvals are cancelled in its terminal transaction. */
|
|
@@ -39,6 +42,8 @@ export declare class RunFinish {
|
|
|
39
42
|
constructor(kdb: KernelDb, eventLog: EventLog, txHost: TransactionHost,
|
|
40
43
|
/** A scheduled task's run follows its kernel run's end in the same transaction (spec §6). */
|
|
41
44
|
taskRuns: Pick<ScheduledTaskRunTracker, "onRunFinished">,
|
|
45
|
+
/** The run's git tickets are revoked and its fetch summaries written after the commit. */
|
|
46
|
+
gitRunEnd: Pick<GitRunEnd, "onRunFinished">,
|
|
42
47
|
/** The run's end owes the conversation's ensure-run backstop (transactional outbox spec §6). */
|
|
43
48
|
outbox: Pick<OutboxService, "add">,
|
|
44
49
|
/** A finished run's pending approvals are cancelled in its terminal transaction. */
|
|
@@ -11,6 +11,8 @@ import { Inject, Injectable } from "@nestjs/common";
|
|
|
11
11
|
import { sql } from "kysely";
|
|
12
12
|
import { EventLog } from "../conversations/event-log.js";
|
|
13
13
|
import { pauseQueueIn } from "../conversations/queue.js";
|
|
14
|
+
import { GitRunEnd } from "../git/run-end.js";
|
|
15
|
+
import { afterCommit } from "../infra/database/after-commit.js";
|
|
14
16
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
15
17
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
16
18
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
@@ -30,12 +32,15 @@ let RunFinish = class RunFinish {
|
|
|
30
32
|
eventLog;
|
|
31
33
|
txHost;
|
|
32
34
|
taskRuns;
|
|
35
|
+
gitRunEnd;
|
|
33
36
|
outbox;
|
|
34
37
|
approvals;
|
|
35
38
|
questions;
|
|
36
39
|
constructor(kdb, eventLog, txHost,
|
|
37
40
|
/** A scheduled task's run follows its kernel run's end in the same transaction (spec §6). */
|
|
38
41
|
taskRuns,
|
|
42
|
+
/** The run's git tickets are revoked and its fetch summaries written after the commit. */
|
|
43
|
+
gitRunEnd,
|
|
39
44
|
/** The run's end owes the conversation's ensure-run backstop (transactional outbox spec §6). */
|
|
40
45
|
outbox,
|
|
41
46
|
/** A finished run's pending approvals are cancelled in its terminal transaction. */
|
|
@@ -46,6 +51,7 @@ let RunFinish = class RunFinish {
|
|
|
46
51
|
this.eventLog = eventLog;
|
|
47
52
|
this.txHost = txHost;
|
|
48
53
|
this.taskRuns = taskRuns;
|
|
54
|
+
this.gitRunEnd = gitRunEnd;
|
|
49
55
|
this.outbox = outbox;
|
|
50
56
|
this.approvals = approvals;
|
|
51
57
|
this.questions = questions;
|
|
@@ -122,6 +128,8 @@ let RunFinish = class RunFinish {
|
|
|
122
128
|
.where("id", "=", a.runId)
|
|
123
129
|
.execute();
|
|
124
130
|
await this.taskRuns.onRunFinished(a.runId, a.status, failureMessage(a.event));
|
|
131
|
+
// the terminal status is committed first, then the tickets go (the one place runs end)
|
|
132
|
+
afterCommit(() => this.gitRunEnd.onRunFinished(a.runId));
|
|
125
133
|
// a failed or stopped run pauses the queued messages until the person resumes (spec §5.7)
|
|
126
134
|
if (a.status !== "completed")
|
|
127
135
|
await pauseQueueIn(t, a.conversationId);
|
|
@@ -185,9 +193,10 @@ RunFinish = __decorate([
|
|
|
185
193
|
__param(1, Inject(EventLog)),
|
|
186
194
|
__param(2, Inject(TransactionHost)),
|
|
187
195
|
__param(3, Inject(ScheduledTaskRunTracker)),
|
|
188
|
-
__param(4, Inject(
|
|
189
|
-
__param(5, Inject(
|
|
190
|
-
__param(6, Inject(
|
|
196
|
+
__param(4, Inject(GitRunEnd)),
|
|
197
|
+
__param(5, Inject(OutboxService)),
|
|
198
|
+
__param(6, Inject(ApprovalsService)),
|
|
199
|
+
__param(7, Inject(QuestionsService))
|
|
191
200
|
], RunFinish);
|
|
192
201
|
export { RunFinish };
|
|
193
202
|
/**
|
|
@@ -12,6 +12,7 @@ import { AttachmentsModule } from "../conversations/attachments.module.js";
|
|
|
12
12
|
import { EventLogModule } from "../conversations/event-log.js";
|
|
13
13
|
import { TitlesModule } from "../conversations/titles.js";
|
|
14
14
|
import { ExtensionsModule } from "../extensions/extensions.module.js";
|
|
15
|
+
import { GitModule } from "../git/git.module.js";
|
|
15
16
|
import { IdentityModule } from "../identity/identity.module.js";
|
|
16
17
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
17
18
|
import { QueueModule } from "../infra/queue/queue.module.js";
|
|
@@ -58,6 +59,7 @@ RuntimeModule = __decorate([
|
|
|
58
59
|
IdentityModule,
|
|
59
60
|
SkillsModule,
|
|
60
61
|
ToolsModule,
|
|
62
|
+
GitModule,
|
|
61
63
|
ProvidersModule,
|
|
62
64
|
RunJobsModule,
|
|
63
65
|
SubagentsModule,
|
package/dist/runtime/waits.js
CHANGED
|
@@ -19,7 +19,7 @@ import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
|
19
19
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
20
20
|
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
21
21
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
22
|
-
import { ApprovalsService, NETWORK_ASK_CANCELLED_NOTE, } from "../tools/approvals.service.js";
|
|
22
|
+
import { ApprovalsService, GIT_ASK_CANCELLED_NOTE, NETWORK_ASK_CANCELLED_NOTE, } from "../tools/approvals.service.js";
|
|
23
23
|
import { APPROVAL_CANCELLED_TEXT, INPUT_CANCELLED_TEXT, RUN_STOPPED_TEXT, ToolPipeline, } from "../tools/pipeline.js";
|
|
24
24
|
import { QuestionsService } from "../tools/questions.service.js";
|
|
25
25
|
import { RunAbort } from "./abort.js";
|
|
@@ -338,13 +338,15 @@ let Waits = class Waits {
|
|
|
338
338
|
.orderBy("attempt")
|
|
339
339
|
.execute();
|
|
340
340
|
for (const a of approvals) {
|
|
341
|
-
// a network ask's command already ran: its kept result, then why access stays
|
|
342
|
-
// (network ask-to-allow spec §6.7)
|
|
343
|
-
const kept = a.kind === "network"
|
|
341
|
+
// a network or git ask's command already ran: its kept result, then why access stays
|
|
342
|
+
// blocked or the push was not made (network ask-to-allow spec §6.7)
|
|
343
|
+
const kept = a.kind === "network" || a.kind === "git"
|
|
344
|
+
? a.detail?.result
|
|
345
|
+
: null;
|
|
344
346
|
cancelled.set(a.call_id, kept
|
|
345
347
|
? {
|
|
346
348
|
toolKey: a.tool_key,
|
|
347
|
-
text: `${kept.content}\n\n${NETWORK_ASK_CANCELLED_NOTE}`,
|
|
349
|
+
text: `${kept.content}\n\n${a.kind === "git" ? GIT_ASK_CANCELLED_NOTE : NETWORK_ASK_CANCELLED_NOTE}`,
|
|
348
350
|
isError: kept.isError,
|
|
349
351
|
}
|
|
350
352
|
: { toolKey: a.tool_key, text: APPROVAL_CANCELLED_TEXT });
|
package/dist/schedules/fire.d.ts
CHANGED
|
@@ -25,9 +25,9 @@ export declare function localTimeOf(at: Date, timeZone: string): string;
|
|
|
25
25
|
* count), then the task run and the task rows: the checks in order (task paused since the
|
|
26
26
|
* scan, unless a person's "run now"; a tick fired more than 10 minutes past the org's catch-up
|
|
27
27
|
* window, `missed`, unless a "run now"; org disabled, creator inactive or not a member, agent
|
|
28
|
-
* gone or not visible, conversation gone, connection inactive
|
|
29
|
-
* overlap, rate limit, device offline, queue paused), each a `skipped` run with its reason,
|
|
30
|
-
* lasting ones also pausing the task; then `fired_at` is written, after the rate limit count,
|
|
28
|
+
* gone or not visible, conversation gone, connection inactive or its extension disabled in the
|
|
29
|
+
* org, overlap, rate limit, device offline, queue paused), each a `skipped` run with its reason,
|
|
30
|
+
* the lasting ones also pausing the task; then `fired_at` is written, after the rate limit count,
|
|
31
31
|
* with the run's delayed settle check owed (`sched-settle`, {@link settle}).
|
|
32
32
|
* A run whose claim committed before (a retried job) goes straight to the start.
|
|
33
33
|
* 2. The start, idempotent: `new` creates the conversation with its first message keyed by the
|
package/dist/schedules/fire.js
CHANGED
|
@@ -72,9 +72,9 @@ const DEVICE_GONE = new Set(["device.not_found", "device.unavailable", "sandbox.
|
|
|
72
72
|
* count), then the task run and the task rows: the checks in order (task paused since the
|
|
73
73
|
* scan, unless a person's "run now"; a tick fired more than 10 minutes past the org's catch-up
|
|
74
74
|
* window, `missed`, unless a "run now"; org disabled, creator inactive or not a member, agent
|
|
75
|
-
* gone or not visible, conversation gone, connection inactive
|
|
76
|
-
* overlap, rate limit, device offline, queue paused), each a `skipped` run with its reason,
|
|
77
|
-
* lasting ones also pausing the task; then `fired_at` is written, after the rate limit count,
|
|
75
|
+
* gone or not visible, conversation gone, connection inactive or its extension disabled in the
|
|
76
|
+
* org, overlap, rate limit, device offline, queue paused), each a `skipped` run with its reason,
|
|
77
|
+
* the lasting ones also pausing the task; then `fired_at` is written, after the rate limit count,
|
|
78
78
|
* with the run's delayed settle check owed (`sched-settle`, {@link settle}).
|
|
79
79
|
* A run whose claim committed before (a retried job) goes straight to the start.
|
|
80
80
|
* 2. The start, idempotent: `new` creates the conversation with its first message keyed by the
|
|
@@ -279,10 +279,13 @@ let ScheduledTaskFirer = class ScheduledTaskFirer {
|
|
|
279
279
|
!conn ||
|
|
280
280
|
conn.orgId !== orgId ||
|
|
281
281
|
conn.status !== "active" ||
|
|
282
|
-
!conn.pluginEnabled ||
|
|
283
282
|
conn.agentId !== task.agentId) {
|
|
284
283
|
return skip("target_gone");
|
|
285
284
|
}
|
|
285
|
+
// the org switched the channel's extension off: the task stays active and recovers when it
|
|
286
|
+
// is switched on again (unlike a target that is gone)
|
|
287
|
+
if (!conn.pluginEnabled)
|
|
288
|
+
return skip("plugin_disabled");
|
|
286
289
|
if (chat.chatType === "group") {
|
|
287
290
|
// a group has no current conversation: each run starts its own thread at its first post
|
|
288
291
|
// (spec §8), which needs a channel with threads; the creator must have written there
|
|
@@ -71,6 +71,35 @@ export declare function networkGrantRefusal(host: string, ctx: NetworkGrantConte
|
|
|
71
71
|
code: GrantRefusalCode;
|
|
72
72
|
message: string;
|
|
73
73
|
} | null;
|
|
74
|
+
/**
|
|
75
|
+
* A git push grant (sandbox git credentials spec §7): `git:push` lets the task's unattended runs
|
|
76
|
+
* push to any repository the creator's git source reaches, `git:push:<owner>/<repo>` to that one.
|
|
77
|
+
*/
|
|
78
|
+
export declare const GIT_PUSH_GRANT = "git:push";
|
|
79
|
+
/** The prefix of a per-repository git push grant (`git:push:<owner>/<repo>`). */
|
|
80
|
+
export declare const GIT_PUSH_REPO_GRANT_PREFIX = "git:push:";
|
|
81
|
+
/** The `spec_hash` a git push grant is stored with (no tool definition behind it). */
|
|
82
|
+
export declare const GIT_GRANT_SPEC_HASH = "git";
|
|
83
|
+
/**
|
|
84
|
+
* A repository as policies, grants and asks key it: `<owner>/<repo>` lower-cased (hosts compare
|
|
85
|
+
* repositories without case), a trailing `.git` dropped; null when it is not of that shape or a
|
|
86
|
+
* segment is `.` / `..`.
|
|
87
|
+
*/
|
|
88
|
+
export declare function normalizeGitRepo(repo: string): string | null;
|
|
89
|
+
/** Whether a grant key is a git push grant (`git:push` or `git:push:<owner>/<repo>`). */
|
|
90
|
+
export declare const isGitGrantKey: (key: string) => boolean;
|
|
91
|
+
/** The grant key of pushes to `repo` (normalized by the caller), or to any repository. */
|
|
92
|
+
export declare const gitPushGrantKey: (repo?: string) => string;
|
|
93
|
+
/**
|
|
94
|
+
* Why a git push grant key cannot be one of a task's grants, or null when it can: the org forbids
|
|
95
|
+
* it (`forbiddenTools` lists the key or `git:push`) or the repository is not `<owner>/<repo>`
|
|
96
|
+
* (`not_grantable`). The key is compared as given (callers normalize it with
|
|
97
|
+
* {@link normalizeGitGrantKey} first).
|
|
98
|
+
*/
|
|
99
|
+
export declare function gitGrantRefusal(key: string, ctx: Pick<GrantContext, "forbiddenTools">): {
|
|
100
|
+
code: GrantRefusalCode;
|
|
101
|
+
message: string;
|
|
102
|
+
} | null;
|
|
74
103
|
/** The prefix of a sandbox tier grant's key (sandbox dev tiers spec §11.4): only `tier:open` exists. */
|
|
75
104
|
export declare const TIER_GRANT_PREFIX = "tier:";
|
|
76
105
|
/** Whether a grant key is a sandbox tier grant (`tier:<tier>`). */
|
|
@@ -92,3 +121,14 @@ export declare function tierGrantRefusal(key: string, ctx: {
|
|
|
92
121
|
code: GrantRefusalCode;
|
|
93
122
|
message: string;
|
|
94
123
|
} | null;
|
|
124
|
+
/**
|
|
125
|
+
* Whether the org's `forbiddenTools` forbid the git push grant `key` (normalized): it lists
|
|
126
|
+
* `git:push` or the key, each entry compared normalized (`git:push:Acme/App` forbids
|
|
127
|
+
* `git:push:acme/app`).
|
|
128
|
+
*/
|
|
129
|
+
export declare function gitPushForbidden(forbiddenTools: readonly string[], key: string): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* A git push grant key as stored: the repository normalized ({@link normalizeGitRepo}); the key
|
|
132
|
+
* unchanged when its repository is not of the shape (then {@link gitGrantRefusal} refuses it).
|
|
133
|
+
*/
|
|
134
|
+
export declare function normalizeGitGrantKey(key: string): string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { validateAllowHost, } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { GIT_REPO_MAX_CHARS } from "../conversations/events.js";
|
|
2
3
|
import { orgAllows } from "../local-devices/network-grants.js";
|
|
3
4
|
import { roleAtLeast } from "../orgs/roles.js";
|
|
4
5
|
import { isFixedAsk } from "../tools/fixed-ask.js";
|
|
@@ -13,6 +14,9 @@ export { TIER_GRANT_SPEC_HASH, TIER_OPEN_GRANT_KEY };
|
|
|
13
14
|
* undefined when the agent does not have it now.
|
|
14
15
|
*/
|
|
15
16
|
export function grantRefusalOf(toolKey, tool, ctx) {
|
|
17
|
+
// a git push grant belongs to no tool (sandbox git credentials spec §7)
|
|
18
|
+
if (isGitGrantKey(toolKey))
|
|
19
|
+
return gitGrantRefusal(toolKey, ctx);
|
|
16
20
|
if (ctx.forbiddenTools.includes(toolKey) ||
|
|
17
21
|
(tool !== undefined && ctx.forbiddenTools.includes(tool.key))) {
|
|
18
22
|
return {
|
|
@@ -89,6 +93,61 @@ export function networkGrantRefusal(host, ctx) {
|
|
|
89
93
|
}
|
|
90
94
|
return null;
|
|
91
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* A git push grant (sandbox git credentials spec §7): `git:push` lets the task's unattended runs
|
|
98
|
+
* push to any repository the creator's git source reaches, `git:push:<owner>/<repo>` to that one.
|
|
99
|
+
*/
|
|
100
|
+
export const GIT_PUSH_GRANT = "git:push";
|
|
101
|
+
/** The prefix of a per-repository git push grant (`git:push:<owner>/<repo>`). */
|
|
102
|
+
export const GIT_PUSH_REPO_GRANT_PREFIX = `${GIT_PUSH_GRANT}:`;
|
|
103
|
+
/** The `spec_hash` a git push grant is stored with (no tool definition behind it). */
|
|
104
|
+
export const GIT_GRANT_SPEC_HASH = "git";
|
|
105
|
+
/** The shape of `<owner>/<repo>` (one owner segment, one repository segment). */
|
|
106
|
+
const GIT_REPO_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
107
|
+
/**
|
|
108
|
+
* A repository as policies, grants and asks key it: `<owner>/<repo>` lower-cased (hosts compare
|
|
109
|
+
* repositories without case), a trailing `.git` dropped; null when it is not of that shape or a
|
|
110
|
+
* segment is `.` / `..`.
|
|
111
|
+
*/
|
|
112
|
+
export function normalizeGitRepo(repo) {
|
|
113
|
+
const r = repo
|
|
114
|
+
.trim()
|
|
115
|
+
.replace(/\.git$/i, "")
|
|
116
|
+
.toLowerCase();
|
|
117
|
+
if (r.length > GIT_REPO_MAX_CHARS || !GIT_REPO_PATTERN.test(r))
|
|
118
|
+
return null;
|
|
119
|
+
if (r.split("/").some((seg) => seg === "." || seg === ".."))
|
|
120
|
+
return null;
|
|
121
|
+
return r;
|
|
122
|
+
}
|
|
123
|
+
/** Whether a grant key is a git push grant (`git:push` or `git:push:<owner>/<repo>`). */
|
|
124
|
+
export const isGitGrantKey = (key) => key === GIT_PUSH_GRANT || key.startsWith(GIT_PUSH_REPO_GRANT_PREFIX);
|
|
125
|
+
/** The grant key of pushes to `repo` (normalized by the caller), or to any repository. */
|
|
126
|
+
export const gitPushGrantKey = (repo) => repo === undefined ? GIT_PUSH_GRANT : `${GIT_PUSH_REPO_GRANT_PREFIX}${repo}`;
|
|
127
|
+
/**
|
|
128
|
+
* Why a git push grant key cannot be one of a task's grants, or null when it can: the org forbids
|
|
129
|
+
* it (`forbiddenTools` lists the key or `git:push`) or the repository is not `<owner>/<repo>`
|
|
130
|
+
* (`not_grantable`). The key is compared as given (callers normalize it with
|
|
131
|
+
* {@link normalizeGitGrantKey} first).
|
|
132
|
+
*/
|
|
133
|
+
export function gitGrantRefusal(key, ctx) {
|
|
134
|
+
if (gitPushForbidden(ctx.forbiddenTools, key)) {
|
|
135
|
+
return {
|
|
136
|
+
code: "forbidden",
|
|
137
|
+
message: `The organization does not allow ${key} in scheduled runs`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (key === GIT_PUSH_GRANT)
|
|
141
|
+
return null;
|
|
142
|
+
const repo = key.slice(GIT_PUSH_REPO_GRANT_PREFIX.length);
|
|
143
|
+
if (normalizeGitRepo(repo) !== repo) {
|
|
144
|
+
return {
|
|
145
|
+
code: "not_grantable",
|
|
146
|
+
message: `${key} cannot be approved for a scheduled task: name one repository as git:push:<owner>/<repo>`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
92
151
|
/** The prefix of a sandbox tier grant's key (sandbox dev tiers spec §11.4): only `tier:open` exists. */
|
|
93
152
|
export const TIER_GRANT_PREFIX = "tier:";
|
|
94
153
|
/** Whether a grant key is a sandbox tier grant (`tier:<tier>`). */
|
|
@@ -124,3 +183,27 @@ export function tierGrantRefusal(key, ctx) {
|
|
|
124
183
|
}
|
|
125
184
|
return null;
|
|
126
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Whether the org's `forbiddenTools` forbid the git push grant `key` (normalized): it lists
|
|
188
|
+
* `git:push` or the key, each entry compared normalized (`git:push:Acme/App` forbids
|
|
189
|
+
* `git:push:acme/app`).
|
|
190
|
+
*/
|
|
191
|
+
export function gitPushForbidden(forbiddenTools, key) {
|
|
192
|
+
return forbiddenTools.some((entry) => {
|
|
193
|
+
const lowered = entry.trim().toLowerCase();
|
|
194
|
+
if (!isGitGrantKey(lowered))
|
|
195
|
+
return false;
|
|
196
|
+
const e = normalizeGitGrantKey(lowered);
|
|
197
|
+
return e === GIT_PUSH_GRANT || e === key;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* A git push grant key as stored: the repository normalized ({@link normalizeGitRepo}); the key
|
|
202
|
+
* unchanged when its repository is not of the shape (then {@link gitGrantRefusal} refuses it).
|
|
203
|
+
*/
|
|
204
|
+
export function normalizeGitGrantKey(key) {
|
|
205
|
+
if (key === GIT_PUSH_GRANT || !key.startsWith(GIT_PUSH_REPO_GRANT_PREFIX))
|
|
206
|
+
return key;
|
|
207
|
+
const repo = normalizeGitRepo(key.slice(GIT_PUSH_REPO_GRANT_PREFIX.length));
|
|
208
|
+
return repo === null ? key : gitPushGrantKey(repo);
|
|
209
|
+
}
|
|
@@ -255,9 +255,9 @@ export declare class ScheduledTasksService {
|
|
|
255
255
|
/** Each task with its latest settled non-trial run, in one query. */
|
|
256
256
|
private withLastRuns;
|
|
257
257
|
/**
|
|
258
|
-
* The chats the caller can point a `chat` task at (spec §8): per active connection of the org
|
|
259
|
-
* their DM threads (archived included) and, on a channel with
|
|
260
|
-
* thread in. Members up (`schedules.use`, the route's).
|
|
258
|
+
* The chats the caller can point a `chat` task at (spec §8): per active connection of the org
|
|
259
|
+
* whose extension is enabled in it, their DM threads (archived included) and, on a channel with
|
|
260
|
+
* threads, the groups they have a thread in. Members up (`schedules.use`, the route's).
|
|
261
261
|
*/
|
|
262
262
|
chatTargets(orgId: string, accountId: string): Promise<ChatTarget[]>;
|
|
263
263
|
/**
|
|
@@ -322,9 +322,9 @@ export declare class ScheduledTasksService {
|
|
|
322
322
|
* - the mode and its fields agree (`conversation`: a conversation and no chat; `chat`: a chat
|
|
323
323
|
* and no conversation; `new`: neither);
|
|
324
324
|
* - `conversation`: the creator's live chat conversation with this agent, still accessible;
|
|
325
|
-
* - `chat`: the connection is active
|
|
326
|
-
*
|
|
327
|
-
* and a channel with threads.
|
|
325
|
+
* - `chat`: the connection is active, its channel extension is enabled in this org, and it is
|
|
326
|
+
* bound to this agent; a DM needs a thread row (archived allowed) of the creator in that chat;
|
|
327
|
+
* a group needs the creator's thread row in the group and a channel with threads.
|
|
328
328
|
*/
|
|
329
329
|
private checkTarget;
|
|
330
330
|
/** The channel's declared features; undefined when its extension is not loaded here. */
|
|
@@ -347,7 +347,9 @@ export declare class ScheduledTasksService {
|
|
|
347
347
|
* `deviceId`, the task's device (null: none can be determined, the device rule is skipped),
|
|
348
348
|
* stored with `spec_hash` `network` and no connector (plan P-D7), and only when
|
|
349
349
|
* `source.fromSession` (a person on the task page; never a token, the Platform tool or the
|
|
350
|
-
* schedule tools).
|
|
350
|
+
* schedule tools). Each git push key (`git:push`, `git:push:<owner>/<repo>`, the repository
|
|
351
|
+
* lower-cased) is refused by {@link gitGrantRefusal} and stored with `spec_hash` `git` and no
|
|
352
|
+
* connector, also only when `source.fromSession`. Duplicates keep the last.
|
|
351
353
|
*/
|
|
352
354
|
private resolveGrants;
|
|
353
355
|
/**
|