@capybara-hq/kernel 0.1.0-rc.26 → 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 +18 -1
- package/dist/catalog/catalog-content.js +5 -2
- 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 +13 -3
- package/dist/catalog/plugin-install.d.ts +37 -16
- package/dist/catalog/plugin-install.js +108 -34
- 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 -9
- 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
|
@@ -73,19 +73,52 @@ const RUN_AT_SCHEMA = {
|
|
|
73
73
|
};
|
|
74
74
|
const MODE_SCHEMA = {
|
|
75
75
|
type: "string",
|
|
76
|
-
enum: ["new", "here"],
|
|
76
|
+
enum: ["new", "here", "chat"],
|
|
77
77
|
description: '"new": every run starts a new conversation with this agent. "here": every run posts the ' +
|
|
78
78
|
"prompt into this conversation (in an IM direct chat: the chat's current conversation), " +
|
|
79
|
-
'e.g. for "check this again at 3 pm".'
|
|
79
|
+
'e.g. for "check this again at 3 pm". "chat": every run posts into the IM chat named by ' +
|
|
80
|
+
"chat.",
|
|
81
|
+
};
|
|
82
|
+
const CHAT_SCHEMA = {
|
|
83
|
+
type: "object",
|
|
84
|
+
properties: {
|
|
85
|
+
connectionId: {
|
|
86
|
+
type: "string",
|
|
87
|
+
minLength: 1,
|
|
88
|
+
maxLength: 100,
|
|
89
|
+
description: "The id of the active channel connection that delivers to the chat.",
|
|
90
|
+
},
|
|
91
|
+
chatId: {
|
|
92
|
+
type: "string",
|
|
93
|
+
minLength: 1,
|
|
94
|
+
maxLength: 500,
|
|
95
|
+
description: "The chat's id within that connection, e.g. a DM's user id or a group's id.",
|
|
96
|
+
},
|
|
97
|
+
chatType: {
|
|
98
|
+
type: "string",
|
|
99
|
+
enum: ["dm", "group"],
|
|
100
|
+
description: 'The kind of chat: "dm" (the default) or "group". A group needs a channel with threads.',
|
|
101
|
+
},
|
|
102
|
+
agentId: {
|
|
103
|
+
type: "string",
|
|
104
|
+
minLength: 1,
|
|
105
|
+
maxLength: 100,
|
|
106
|
+
description: "The agent the chat belongs to; defaults to this run's agent. A chat target must use " +
|
|
107
|
+
"the connection's agent.",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
required: ["connectionId", "chatId"],
|
|
111
|
+
additionalProperties: false,
|
|
80
112
|
};
|
|
81
113
|
const CREATE_SPEC = {
|
|
82
114
|
name: BUILTIN.ScheduleCreate,
|
|
83
115
|
description: "Create a scheduled task for the user: this agent runs the prompt on a schedule (cron) or " +
|
|
84
|
-
"once at a time (runAt), in the user's time zone, acting as the user.
|
|
85
|
-
|
|
86
|
-
'
|
|
87
|
-
|
|
88
|
-
"
|
|
116
|
+
"once at a time (runAt), in the user's time zone, acting as the user. Each run starts a new " +
|
|
117
|
+
"conversation, posts into this conversation, or posts into an IM chat the user names with " +
|
|
118
|
+
'chat. Use it when the user asks for something recurring or later ("every weekday at 9 ' +
|
|
119
|
+
'send me a brief", "remind me at 3 pm"). Every call asks the user to approve the whole task ' +
|
|
120
|
+
"first. Runs happen without the user present: write the prompt so it stands on its own, and " +
|
|
121
|
+
"list in approvedTools the tools a run needs without asking. Do not use it for work to do now.",
|
|
89
122
|
keyArgs: ["name"],
|
|
90
123
|
inputSchema: {
|
|
91
124
|
type: "object",
|
|
@@ -105,6 +138,10 @@ const CREATE_SPEC = {
|
|
|
105
138
|
cron: CRON_SCHEMA,
|
|
106
139
|
runAt: RUN_AT_SCHEMA,
|
|
107
140
|
mode: MODE_SCHEMA,
|
|
141
|
+
chat: {
|
|
142
|
+
...CHAT_SCHEMA,
|
|
143
|
+
description: 'For mode "chat": the IM chat every run posts into.',
|
|
144
|
+
},
|
|
108
145
|
approvedTools: TOOL_KEYS_SCHEMA,
|
|
109
146
|
},
|
|
110
147
|
required: ["name", "prompt", "mode"],
|
|
@@ -113,11 +150,10 @@ const CREATE_SPEC = {
|
|
|
113
150
|
};
|
|
114
151
|
const UPDATE_SPEC = {
|
|
115
152
|
name: BUILTIN.ScheduleUpdate,
|
|
116
|
-
description: "Change one of the user's scheduled tasks: its name, prompt, schedule, where it runs
|
|
117
|
-
"
|
|
118
|
-
"to change; approvedTools replaces the whole list. Every
|
|
119
|
-
"
|
|
120
|
-
"the task page.",
|
|
153
|
+
description: "Change one of the user's scheduled tasks: its name, prompt, schedule, where it runs (a new " +
|
|
154
|
+
"conversation, this conversation or an IM chat, and the agent) or its approved tools. Pass " +
|
|
155
|
+
"the task id and only the fields to change; approvedTools replaces the whole list. Every " +
|
|
156
|
+
"call asks the user to approve the change first. It does not pause, resume or delete a task.",
|
|
121
157
|
keyArgs: ["id"],
|
|
122
158
|
inputSchema: {
|
|
123
159
|
type: "object",
|
|
@@ -133,6 +169,10 @@ const UPDATE_SPEC = {
|
|
|
133
169
|
cron: CRON_SCHEMA,
|
|
134
170
|
runAt: RUN_AT_SCHEMA,
|
|
135
171
|
mode: MODE_SCHEMA,
|
|
172
|
+
chat: {
|
|
173
|
+
...CHAT_SCHEMA,
|
|
174
|
+
description: 'For mode "chat": the IM chat every run posts into; replaces the target.',
|
|
175
|
+
},
|
|
136
176
|
approvedTools: {
|
|
137
177
|
...TOOL_KEYS_SCHEMA,
|
|
138
178
|
description: `${TOOL_KEYS_SCHEMA.description} Replaces the task's whole list; pass [] to remove every approved tool, omit it to keep the list.`,
|
|
@@ -145,15 +185,21 @@ const UPDATE_SPEC = {
|
|
|
145
185
|
const LIST_SPEC = {
|
|
146
186
|
name: BUILTIN.ScheduleList,
|
|
147
187
|
description: "List the user's scheduled tasks in this organization: id, name, schedule with its time " +
|
|
148
|
-
"zone, status, next run and where runs go.
|
|
149
|
-
|
|
188
|
+
"zone, status, next run and where runs go. Read-only.",
|
|
189
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
190
|
+
};
|
|
191
|
+
const TARGETS_SPEC = {
|
|
192
|
+
name: BUILTIN.ScheduleTargets,
|
|
193
|
+
description: "List the IM chats scheduled task runs can be delivered to for the user in this " +
|
|
194
|
+
"organization: each chat's connection id, chat id, chat type and the connection's agent id, " +
|
|
195
|
+
"with a label naming the connection. Read-only.",
|
|
150
196
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
151
197
|
};
|
|
152
198
|
const DELETE_SPEC = {
|
|
153
199
|
name: BUILTIN.ScheduleDelete,
|
|
154
200
|
description: "Delete one of the user's scheduled tasks for good: it never runs again; past conversations " +
|
|
155
|
-
"stay. Use it when the user asks to stop or remove a task
|
|
156
|
-
"
|
|
201
|
+
"stay. Use it when the user asks to stop or remove a task. Only the user's own tasks can be " +
|
|
202
|
+
"deleted.",
|
|
157
203
|
keyArgs: ["id"],
|
|
158
204
|
inputSchema: {
|
|
159
205
|
type: "object",
|
|
@@ -165,7 +211,15 @@ const DELETE_SPEC = {
|
|
|
165
211
|
},
|
|
166
212
|
};
|
|
167
213
|
const ToolKeys = z.array(z.string().min(1).max(200)).max(TOOLS_MAX);
|
|
168
|
-
const Mode = z.enum(["new", "here"]);
|
|
214
|
+
const Mode = z.enum(["new", "here", "chat"]);
|
|
215
|
+
const Chat = z
|
|
216
|
+
.object({
|
|
217
|
+
connectionId: z.string().min(1).max(100),
|
|
218
|
+
chatId: z.string().min(1).max(500),
|
|
219
|
+
chatType: z.enum(["dm", "group"]).optional(),
|
|
220
|
+
agentId: z.string().min(1).max(100).optional(),
|
|
221
|
+
})
|
|
222
|
+
.strict();
|
|
169
223
|
const CreateInput = z
|
|
170
224
|
.object({
|
|
171
225
|
name: z.string().min(1).max(NAME_MAX),
|
|
@@ -173,6 +227,7 @@ const CreateInput = z
|
|
|
173
227
|
cron: z.string().min(1).max(100).optional(),
|
|
174
228
|
runAt: z.string().min(1).max(40).optional(),
|
|
175
229
|
mode: Mode,
|
|
230
|
+
chat: Chat.optional(),
|
|
176
231
|
approvedTools: ToolKeys.optional(),
|
|
177
232
|
})
|
|
178
233
|
.strict();
|
|
@@ -184,19 +239,24 @@ const UpdateInput = z
|
|
|
184
239
|
cron: z.string().min(1).max(100).optional(),
|
|
185
240
|
runAt: z.string().min(1).max(40).optional(),
|
|
186
241
|
mode: Mode.optional(),
|
|
242
|
+
chat: Chat.optional(),
|
|
187
243
|
approvedTools: ToolKeys.optional(),
|
|
188
244
|
})
|
|
189
245
|
.strict();
|
|
190
246
|
const IdInput = z.object({ id: z.string().min(1).max(100) }).strict();
|
|
191
247
|
// ---- model-facing texts (English, actionable) ----
|
|
192
|
-
const INVALID_CREATE_TEXT = "Invalid input; pass name, prompt, mode (new or
|
|
193
|
-
"
|
|
248
|
+
const INVALID_CREATE_TEXT = "Invalid input; pass name, prompt, mode (new, here or chat), exactly one of cron or runAt, " +
|
|
249
|
+
"chat with mode chat (connectionId, chatId, chatType, agentId), and optionally approvedTools " +
|
|
250
|
+
"as an array of tool keys.";
|
|
194
251
|
const INVALID_UPDATE_TEXT = "Invalid input; pass the task id and the fields to change (name, prompt, cron or runAt, mode, " +
|
|
195
|
-
"approvedTools).";
|
|
252
|
+
"chat, approvedTools).";
|
|
196
253
|
const INVALID_ID_TEXT = "Invalid input; pass the task id from ScheduleList.";
|
|
197
254
|
const ONE_TRIGGER_TEXT = "Pass exactly one of cron (a repeating schedule) or runAt (a single run), not both and not neither.";
|
|
198
255
|
const BOTH_TRIGGERS_TEXT = "Pass at most one of cron or runAt: cron makes the task repeat, runAt makes it run once.";
|
|
199
|
-
const NOTHING_TO_UPDATE_TEXT = "Nothing to change; pass at least one of name, prompt, cron, runAt, mode or
|
|
256
|
+
const NOTHING_TO_UPDATE_TEXT = "Nothing to change; pass at least one of name, prompt, cron, runAt, mode, chat or " +
|
|
257
|
+
"approvedTools with the id.";
|
|
258
|
+
const CHAT_NEEDS_CHAT_TEXT = 'mode "chat" needs a chat; pass chat with connectionId and chatId (and optionally chatType and agentId).';
|
|
259
|
+
const CHAT_ONLY_TEXT = 'chat applies only to mode "chat"; pass mode "chat" with it, or omit it.';
|
|
200
260
|
export const SCHEDULES_UNAVAILABLE_TEXT = "Scheduled tasks are not available on this server; ask the user to manage the task on the scheduled tasks page instead.";
|
|
201
261
|
export const SCHEDULES_OFF_TEXT = "Scheduled tasks are turned off in this organization; ask an org admin to turn them on.";
|
|
202
262
|
/** The card recomputed at run time differs from the one the person approved. */
|
|
@@ -226,7 +286,7 @@ export function scheduleErrorText(e, ctx) {
|
|
|
226
286
|
case "schedule.invalid_time_zone":
|
|
227
287
|
return `${m}. Ask the user to set their time zone in their profile, then retry.`;
|
|
228
288
|
case "schedule.target_invalid":
|
|
229
|
-
return `${m}.
|
|
289
|
+
return `${m}. Fix the target and retry, or use mode "new" to start a new conversation for every run instead.`;
|
|
230
290
|
case "schedule.grant_not_allowed":
|
|
231
291
|
return `${m}. Remove it from approvedTools and call again.`;
|
|
232
292
|
case "forbidden":
|
|
@@ -287,7 +347,9 @@ function targetWords(t, currentConversationId) {
|
|
|
287
347
|
return { zh: "每次运行新建对话", en: "a new conversation for every run" };
|
|
288
348
|
if (t.mode === "chat") {
|
|
289
349
|
const n = clip(t.connectionName, 60);
|
|
290
|
-
return
|
|
350
|
+
return t.chat.chatType === "group"
|
|
351
|
+
? { zh: `${n} 的群聊`, en: `the group chat on ${n}` }
|
|
352
|
+
: { zh: `${n} 的私聊`, en: `the direct chat on ${n}` };
|
|
291
353
|
}
|
|
292
354
|
return t.conversationId === currentConversationId
|
|
293
355
|
? { zh: "当前对话", en: "this conversation" }
|
|
@@ -298,16 +360,25 @@ function toolsWords(keys) {
|
|
|
298
360
|
? { zh: "无", en: "none" }
|
|
299
361
|
: { zh: keys.join("、"), en: keys.join(", ") };
|
|
300
362
|
}
|
|
363
|
+
/** The `chat` input with its default kind (`dm`). */
|
|
364
|
+
const normalizedChat = (chat) => ({
|
|
365
|
+
connectionId: chat.connectionId,
|
|
366
|
+
chatId: chat.chatId,
|
|
367
|
+
chatType: chat.chatType ?? "dm",
|
|
368
|
+
});
|
|
369
|
+
/** A chat target with its connection's name for the words. */
|
|
370
|
+
async function chatTargetOf(db, chat) {
|
|
371
|
+
const c = await db
|
|
372
|
+
.selectFrom("channel_connections")
|
|
373
|
+
.select("name")
|
|
374
|
+
.where("id", "=", chat.connectionId)
|
|
375
|
+
.executeTakeFirst();
|
|
376
|
+
return { mode: "chat", chat, connectionName: c?.name ?? chat.connectionId };
|
|
377
|
+
}
|
|
301
378
|
/** A stored task's target; a chat's connection name is read for the words. */
|
|
302
379
|
async function storedTarget(db, task) {
|
|
303
|
-
if (task.mode === "chat" && task.chat !== null)
|
|
304
|
-
|
|
305
|
-
.selectFrom("channel_connections")
|
|
306
|
-
.select("name")
|
|
307
|
-
.where("id", "=", task.chat.connectionId)
|
|
308
|
-
.executeTakeFirst();
|
|
309
|
-
return { mode: "chat", chat: task.chat, connectionName: c?.name ?? task.chat.connectionId };
|
|
310
|
-
}
|
|
380
|
+
if (task.mode === "chat" && task.chat !== null)
|
|
381
|
+
return chatTargetOf(db, task.chat);
|
|
311
382
|
if (task.mode === "conversation" && task.conversationId !== null) {
|
|
312
383
|
return { mode: "conversation", conversationId: task.conversationId };
|
|
313
384
|
}
|
|
@@ -351,7 +422,8 @@ const sameTarget = (a, b) => a.mode === b.mode &&
|
|
|
351
422
|
a.conversationId === b.conversationId) &&
|
|
352
423
|
(a.mode !== "chat" ||
|
|
353
424
|
(a.chat.connectionId === b.chat.connectionId &&
|
|
354
|
-
a.chat.chatId === b.chat.chatId
|
|
425
|
+
a.chat.chatId === b.chat.chatId &&
|
|
426
|
+
a.chat.chatType === b.chat.chatType));
|
|
355
427
|
const uniqueSorted = (keys) => [...new Set(keys)].sort();
|
|
356
428
|
/** cron / runAt as the service's trigger; undefined when neither is given. */
|
|
357
429
|
function triggerOf(i) {
|
|
@@ -361,6 +433,20 @@ function triggerOf(i) {
|
|
|
361
433
|
return { kind: "once", at: i.runAt.trim() };
|
|
362
434
|
return undefined;
|
|
363
435
|
}
|
|
436
|
+
/** `chat` against the mode on create, as a refusal; null when they agree. */
|
|
437
|
+
function createChatError(i) {
|
|
438
|
+
if (i.mode !== "chat" && i.chat !== undefined)
|
|
439
|
+
return new ToolError(CHAT_ONLY_TEXT);
|
|
440
|
+
if (i.mode === "chat" && i.chat === undefined)
|
|
441
|
+
return new ToolError(CHAT_NEEDS_CHAT_TEXT);
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
/** `chat` against the mode on update; null when they agree (or chat is absent). */
|
|
445
|
+
function updateChatError(i) {
|
|
446
|
+
if (i.chat === undefined)
|
|
447
|
+
return null;
|
|
448
|
+
return i.mode !== "chat" ? new ToolError(CHAT_ONLY_TEXT) : null;
|
|
449
|
+
}
|
|
364
450
|
const withZone = (t, timeZone) => t.kind === "cron"
|
|
365
451
|
? { kind: "cron", expr: t.expr, timeZone }
|
|
366
452
|
: { kind: "once", at: t.at, timeZone };
|
|
@@ -429,7 +515,7 @@ ScheduleUpdates = __decorate([
|
|
|
429
515
|
__param(2, Inject(ScheduledTaskStore))
|
|
430
516
|
], ScheduleUpdates);
|
|
431
517
|
export { ScheduleUpdates };
|
|
432
|
-
/** The
|
|
518
|
+
/** The five schedule builtins. */
|
|
433
519
|
export function scheduleTools(deps) {
|
|
434
520
|
/**
|
|
435
521
|
* The service, or a refusal (the tools are hidden then too: {@link unavailable}). Defensive:
|
|
@@ -507,17 +593,28 @@ export function scheduleTools(deps) {
|
|
|
507
593
|
const zone = await deps.timeZoneOf(ctx.orgId, ctx.conversationId);
|
|
508
594
|
const target = input.mode === "here"
|
|
509
595
|
? await hereTarget(deps.db(), ctx.orgId, ctx.accountId, ctx.conversationId)
|
|
510
|
-
:
|
|
511
|
-
|
|
596
|
+
: input.mode === "chat"
|
|
597
|
+
? await chatTargetOf(deps.db(), normalizedChat(input.chat))
|
|
598
|
+
: { mode: "new" };
|
|
599
|
+
return {
|
|
600
|
+
trigger,
|
|
601
|
+
zone,
|
|
602
|
+
target,
|
|
603
|
+
tools: uniqueSorted(input.approvedTools ?? []),
|
|
604
|
+
agentId: input.chat?.agentId ?? ctx.agentId,
|
|
605
|
+
};
|
|
512
606
|
}
|
|
513
|
-
function createCard(input, p, conversationId) {
|
|
607
|
+
function createCard(input, p, conversationId, runAgentId) {
|
|
514
608
|
const when = scheduleWords(withZone(p.trigger, p.zone));
|
|
515
609
|
const where = targetWords(p.target, conversationId);
|
|
516
610
|
const tools = toolsWords(p.tools);
|
|
517
611
|
const name = clip(input.name, NAME_MAX);
|
|
612
|
+
const agent = p.agentId === runAgentId
|
|
613
|
+
? { zh: "", en: "" }
|
|
614
|
+
: { zh: `;以智能体 ${p.agentId} 运行`, en: `; runs as agent ${p.agentId}` };
|
|
518
615
|
return {
|
|
519
|
-
zh: `创建定时任务「${name}」:${when.zh};运行位置:${where.zh};预先批准的工具:${tools.zh}`,
|
|
520
|
-
en: `Create the scheduled task "${name}": ${when.en}; runs in ${where.en}; approved tools: ${tools.en}`,
|
|
616
|
+
zh: `创建定时任务「${name}」:${when.zh};运行位置:${where.zh}${agent.zh};预先批准的工具:${tools.zh}`,
|
|
617
|
+
en: `Create the scheduled task "${name}": ${when.en}; runs in ${where.en}${agent.en}; approved tools: ${tools.en}`,
|
|
521
618
|
};
|
|
522
619
|
}
|
|
523
620
|
const create = {
|
|
@@ -532,24 +629,30 @@ export function scheduleTools(deps) {
|
|
|
532
629
|
if ((parsed.data.cron === undefined) === (parsed.data.runAt === undefined)) {
|
|
533
630
|
return deny(key, ONE_TRIGGER_TEXT);
|
|
534
631
|
}
|
|
632
|
+
const bad = createChatError(parsed.data);
|
|
633
|
+
if (bad !== null)
|
|
634
|
+
return deny(key, bad.message);
|
|
535
635
|
const p = await createPlan(parsed.data, ctx);
|
|
536
|
-
return ask(key, createCard(parsed.data, p, ctx.conversationId));
|
|
636
|
+
return ask(key, createCard(parsed.data, p, ctx.conversationId, ctx.agentId));
|
|
537
637
|
},
|
|
538
638
|
async run(raw, ctx) {
|
|
539
639
|
const parsed = CreateInput.safeParse(raw);
|
|
540
640
|
if (!parsed.success)
|
|
541
641
|
throw new ToolError(INVALID_CREATE_TEXT);
|
|
542
642
|
const input = parsed.data;
|
|
643
|
+
const bad = createChatError(input);
|
|
644
|
+
if (bad !== null)
|
|
645
|
+
throw bad;
|
|
543
646
|
const svc = await service();
|
|
544
647
|
const p = await createPlan(input, ctx);
|
|
545
648
|
if (p.trigger === undefined || (input.cron !== undefined && input.runAt !== undefined)) {
|
|
546
649
|
throw new ToolError(ONE_TRIGGER_TEXT);
|
|
547
650
|
}
|
|
548
651
|
// the zone and target are recomputed now: they must be the ones the person approved
|
|
549
|
-
await sameAsApproved(ctx, BUILTIN.ScheduleCreate, createCard(input, p, ctx.conversationId));
|
|
652
|
+
await sameAsApproved(ctx, BUILTIN.ScheduleCreate, createCard(input, p, ctx.conversationId, ctx.agentId));
|
|
550
653
|
const trigger = p.trigger;
|
|
551
654
|
const task = await call(() => svc.create(ctx.orgId, ctx.accountId, {
|
|
552
|
-
agentId:
|
|
655
|
+
agentId: p.agentId,
|
|
553
656
|
name: input.name,
|
|
554
657
|
prompt: input.prompt,
|
|
555
658
|
trigger,
|
|
@@ -605,7 +708,9 @@ export function scheduleTools(deps) {
|
|
|
605
708
|
const before = await storedTarget(db, task);
|
|
606
709
|
const after = input.mode === "here"
|
|
607
710
|
? await hereTarget(db, ctx.orgId, ctx.accountId, ctx.conversationId)
|
|
608
|
-
:
|
|
711
|
+
: input.mode === "chat"
|
|
712
|
+
? await chatTargetOf(db, normalizedChat(input.chat))
|
|
713
|
+
: { mode: "new" };
|
|
609
714
|
if (!sameTarget(before, after)) {
|
|
610
715
|
const b = targetWords(before, ctx.conversationId);
|
|
611
716
|
const a = targetWords(after, ctx.conversationId);
|
|
@@ -613,6 +718,10 @@ export function scheduleTools(deps) {
|
|
|
613
718
|
en.push(`runs in ${b.en} → ${a.en}`);
|
|
614
719
|
}
|
|
615
720
|
}
|
|
721
|
+
if (input.chat?.agentId !== undefined && input.chat.agentId !== task.agentId) {
|
|
722
|
+
zh.push(`智能体 ${clip(task.agentId, 80)} → ${clip(input.chat.agentId, 80)}`);
|
|
723
|
+
en.push(`agent ${clip(task.agentId, 80)} → ${clip(input.chat.agentId, 80)}`);
|
|
724
|
+
}
|
|
616
725
|
if (input.approvedTools !== undefined) {
|
|
617
726
|
const next = uniqueSorted(input.approvedTools);
|
|
618
727
|
const added = next.filter((k) => !grants.includes(k));
|
|
@@ -649,6 +758,7 @@ export function scheduleTools(deps) {
|
|
|
649
758
|
i.cron === undefined &&
|
|
650
759
|
i.runAt === undefined &&
|
|
651
760
|
i.mode === undefined &&
|
|
761
|
+
i.chat === undefined &&
|
|
652
762
|
i.approvedTools === undefined;
|
|
653
763
|
const update = {
|
|
654
764
|
spec: UPDATE_SPEC,
|
|
@@ -663,6 +773,9 @@ export function scheduleTools(deps) {
|
|
|
663
773
|
if (input.cron !== undefined && input.runAt !== undefined) {
|
|
664
774
|
return deny(key, BOTH_TRIGGERS_TEXT);
|
|
665
775
|
}
|
|
776
|
+
const bad = updateChatError(input);
|
|
777
|
+
if (bad !== null)
|
|
778
|
+
return deny(key, bad.message);
|
|
666
779
|
if (nothingToUpdate(input))
|
|
667
780
|
return deny(key, NOTHING_TO_UPDATE_TEXT);
|
|
668
781
|
const task = await ownTask(ctx.orgId, ctx.accountId, input.id);
|
|
@@ -679,6 +792,9 @@ export function scheduleTools(deps) {
|
|
|
679
792
|
if (input.cron !== undefined && input.runAt !== undefined) {
|
|
680
793
|
throw new ToolError(BOTH_TRIGGERS_TEXT);
|
|
681
794
|
}
|
|
795
|
+
const bad = updateChatError(input);
|
|
796
|
+
if (bad !== null)
|
|
797
|
+
throw bad;
|
|
682
798
|
if (nothingToUpdate(input))
|
|
683
799
|
throw new ToolError(NOTHING_TO_UPDATE_TEXT);
|
|
684
800
|
const svc = await service();
|
|
@@ -697,11 +813,16 @@ export function scheduleTools(deps) {
|
|
|
697
813
|
if (input.mode !== undefined) {
|
|
698
814
|
const target = input.mode === "here"
|
|
699
815
|
? await hereTarget(deps.db(), ctx.orgId, ctx.accountId, ctx.conversationId)
|
|
700
|
-
:
|
|
816
|
+
: input.mode === "chat"
|
|
817
|
+
? await chatTargetOf(deps.db(), normalizedChat(input.chat))
|
|
818
|
+
: { mode: "new" };
|
|
701
819
|
const f = targetFields(target);
|
|
702
820
|
patch.mode = f.mode;
|
|
703
821
|
patch.conversationId = f.conversationId ?? null;
|
|
704
822
|
patch.chat = f.chat ?? null;
|
|
823
|
+
// the chat's agent maps to the service's task agent (the HTTP shape stays flat)
|
|
824
|
+
if (input.chat?.agentId !== undefined)
|
|
825
|
+
patch.agentId = input.chat.agentId;
|
|
705
826
|
}
|
|
706
827
|
const zone = task.trigger.timeZone;
|
|
707
828
|
const updated = await call(() => deps.updates.apply(svc, ctx.orgId, ctx.accountId, task, patch, input.approvedTools), { id: input.id, zone });
|
|
@@ -761,6 +882,43 @@ export function scheduleTools(deps) {
|
|
|
761
882
|
};
|
|
762
883
|
},
|
|
763
884
|
};
|
|
885
|
+
// ---- ScheduleTargets ----
|
|
886
|
+
/** A target's fields in the model-facing line (`ScheduleTargets`). */
|
|
887
|
+
const targetLine = (t) => `- ${clip(t.label, 120)}: connectionId=${t.connectionId}, chatId=${t.chatId}, ` +
|
|
888
|
+
`chatType=${t.chatType}, agentId=${t.agentId} (${clip(t.connectionName, 80)}, ${t.plugin})`;
|
|
889
|
+
const targets = {
|
|
890
|
+
spec: TARGETS_SPEC,
|
|
891
|
+
defaultPolicy: "allow",
|
|
892
|
+
unavailable,
|
|
893
|
+
async run(_raw, ctx) {
|
|
894
|
+
const svc = await service();
|
|
895
|
+
await liveRole(ctx);
|
|
896
|
+
const items = await call(() => svc.chatTargets(ctx.orgId, ctx.accountId), {});
|
|
897
|
+
if (items.length === 0) {
|
|
898
|
+
return {
|
|
899
|
+
content: [
|
|
900
|
+
{
|
|
901
|
+
type: "text",
|
|
902
|
+
text: "The user has no IM chats scheduled task runs can be delivered to in this " +
|
|
903
|
+
"organization; a chat appears once the user has messaged the channel's bot or " +
|
|
904
|
+
"talked in a group.",
|
|
905
|
+
},
|
|
906
|
+
],
|
|
907
|
+
display: display("没有可送达的聊天", "No delivery chats"),
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
return {
|
|
911
|
+
content: [
|
|
912
|
+
{
|
|
913
|
+
type: "text",
|
|
914
|
+
text: `The IM chats this user can deliver scheduled task runs to (${items.length}):\n` +
|
|
915
|
+
items.map(targetLine).join("\n"),
|
|
916
|
+
},
|
|
917
|
+
],
|
|
918
|
+
display: display(`可送达的聊天(${items.length} 个)`, `Listed ${items.length} delivery chats`),
|
|
919
|
+
};
|
|
920
|
+
},
|
|
921
|
+
};
|
|
764
922
|
// ---- ScheduleDelete ----
|
|
765
923
|
const remove = {
|
|
766
924
|
spec: DELETE_SPEC,
|
|
@@ -785,5 +943,5 @@ export function scheduleTools(deps) {
|
|
|
785
943
|
};
|
|
786
944
|
},
|
|
787
945
|
};
|
|
788
|
-
return [create, update, list, remove];
|
|
946
|
+
return [create, update, list, targets, remove];
|
|
789
947
|
}
|
|
@@ -24,6 +24,14 @@ export interface MemberToolPolicyView {
|
|
|
24
24
|
}
|
|
25
25
|
/** Whether a connector's credential is per member: its calls are made with the member's connection. */
|
|
26
26
|
export declare const personalAuth: (auth: unknown) => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The tool name of a member's own policy for pushes to one repository on one host through the
|
|
29
|
+
* connector (sandbox git credentials spec §7: the virtual tool `git_push`,
|
|
30
|
+
* `git_push:<host>/<owner>/<repo>`, so an allow never covers another host). No
|
|
31
|
+
* `connector_tools` row backs it, so the 权限 sheet ({@link ConnectorAccountPolicies.read}) does
|
|
32
|
+
* not list it; 「一直允许」 on a git push ask writes it.
|
|
33
|
+
*/
|
|
34
|
+
export declare const gitPushPolicyName: (host: string, repo: string) => string;
|
|
27
35
|
/**
|
|
28
36
|
* A member's own tool policies for their own connection of a personal-credential connector
|
|
29
37
|
* (maintainer decision 2026-11-09, the connector card's 权限). They apply to every agent the
|
|
@@ -22,6 +22,14 @@ export const personalAuth = (auth) => {
|
|
|
22
22
|
const a = auth;
|
|
23
23
|
return a !== null && a.type !== "none" && a.ownership === "personal";
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* The tool name of a member's own policy for pushes to one repository on one host through the
|
|
27
|
+
* connector (sandbox git credentials spec §7: the virtual tool `git_push`,
|
|
28
|
+
* `git_push:<host>/<owner>/<repo>`, so an allow never covers another host). No
|
|
29
|
+
* `connector_tools` row backs it, so the 权限 sheet ({@link ConnectorAccountPolicies.read}) does
|
|
30
|
+
* not list it; 「一直允许」 on a git push ask writes it.
|
|
31
|
+
*/
|
|
32
|
+
export const gitPushPolicyName = (host, repo) => `git_push:${host.toLowerCase()}/${repo}`;
|
|
25
33
|
const notPersonal = () => new ApiError("connector.not_personal", "This connector uses one credential for the whole organization; only a connector each member connects to with their own credential has per-member tool policies");
|
|
26
34
|
const notFound = () => new ApiError("connector.not_found", "Connector not found");
|
|
27
35
|
/**
|
|
@@ -75,6 +75,7 @@ declare const CreateBody: z.ZodObject<{
|
|
|
75
75
|
tokenTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
76
76
|
}, z.core.$strict>], "type">;
|
|
77
77
|
credential: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
78
|
+
git: z.ZodOptional<z.ZodType<import("@capybara-hq/plugin-sdk").GitSource, unknown, z.core.$ZodTypeInternals<import("@capybara-hq/plugin-sdk").GitSource, unknown>>>;
|
|
78
79
|
}, z.core.$strict>;
|
|
79
80
|
/** 预览(Ruling A20):与创建相同的连接器 / 端点 / 认证 / 凭据,认证可缺省(只读端点描述,不列工具)。 */
|
|
80
81
|
declare const PreviewBody: z.ZodObject<{
|
|
@@ -170,6 +171,7 @@ declare const UpdateBody: z.ZodObject<{
|
|
|
170
171
|
}>>;
|
|
171
172
|
}, z.core.$strict>>>;
|
|
172
173
|
allowUnannotated: z.ZodOptional<z.ZodBoolean>;
|
|
174
|
+
useForGit: z.ZodOptional<z.ZodBoolean>;
|
|
173
175
|
}, z.core.$strict>;
|
|
174
176
|
/**
|
|
175
177
|
* A connector as the org's pages show it: with its plugin's icon (`icon`, see `withIcons`), its
|
|
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import { ConnectorAuthSchema } from "@capybara-hq/plugin-sdk";
|
|
10
|
+
import { ConnectorAuthSchema, GitSourceSchema } from "@capybara-hq/plugin-sdk";
|
|
11
11
|
import { Controller, Delete, Get, HttpCode, Inject, Param, Patch, Post, Put } from "@nestjs/common";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { orgIconScope, PluginIcons } from "../extensions/plugin-icons.js";
|
|
@@ -47,6 +47,8 @@ const CreateBody = z
|
|
|
47
47
|
endpoint: z.record(z.string(), z.unknown()),
|
|
48
48
|
auth: ConnectorAuthSchema,
|
|
49
49
|
credential: CredentialValue.optional(),
|
|
50
|
+
/** The git hosts the credential serves in sandboxes (sandbox git credentials spec §6.1). */
|
|
51
|
+
git: GitSourceSchema.optional(),
|
|
50
52
|
})
|
|
51
53
|
.strict();
|
|
52
54
|
/** 预览(Ruling A20):与创建相同的连接器 / 端点 / 认证 / 凭据,认证可缺省(只读端点描述,不列工具)。 */
|
|
@@ -76,6 +78,8 @@ const UpdateBody = z
|
|
|
76
78
|
.refine((v) => Object.keys(v).length <= 1000, { message: "Too many tools" })
|
|
77
79
|
.optional(),
|
|
78
80
|
allowUnannotated: z.boolean().optional(),
|
|
81
|
+
/** Use an org-credential connector for git in sandboxes (spec 2026-11-12 §6.2). */
|
|
82
|
+
useForGit: z.boolean().optional(),
|
|
79
83
|
})
|
|
80
84
|
.strict();
|
|
81
85
|
class ConnectorCreateBody extends createZodDto(CreateBody, "ConnectorCreateBody") {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AuthHint, type ConnectorOperation, type ToolAnnotations } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type AuthHint, type ConnectorOperation, type GitSource, type ToolAnnotations } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { type ConnectionGuideState, PluginGuides } from "../extensions/plugin-guides.js";
|
|
3
3
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
4
4
|
import type { WriteActor } from "../identity/write-actor.js";
|
|
@@ -71,6 +71,16 @@ export interface ConnectorSummary {
|
|
|
71
71
|
platformTemplateId: string | null;
|
|
72
72
|
/** The plugin (catalog item) its template came from (`connectors.catalog_item_id`), else null. */
|
|
73
73
|
catalogItemId: string | null;
|
|
74
|
+
/**
|
|
75
|
+
* The git hosts its credential serves in sandboxes (`connectors.git_hosts`; sandbox git
|
|
76
|
+
* credentials spec §6.1), null when it declares none.
|
|
77
|
+
*/
|
|
78
|
+
git: GitSource | null;
|
|
79
|
+
/**
|
|
80
|
+
* An org-ownership connector serves as a git source (`connectors.use_for_git`, set by an org
|
|
81
|
+
* admin); personal connectors with `git` always do and keep this false.
|
|
82
|
+
*/
|
|
83
|
+
useForGit: boolean;
|
|
74
84
|
createdBy: string;
|
|
75
85
|
createdAt: Date;
|
|
76
86
|
updatedAt: Date;
|
|
@@ -90,6 +100,11 @@ export interface CreateConnectorInput {
|
|
|
90
100
|
endpoint: unknown;
|
|
91
101
|
auth: ConnectorAuth;
|
|
92
102
|
credential?: unknown;
|
|
103
|
+
/**
|
|
104
|
+
* The git hosts the connector's credential serves in sandboxes (a template's `git`, or the
|
|
105
|
+
* create body's; validated by the SDK's `GitSourceSchema`), stored in `connectors.git_hosts`.
|
|
106
|
+
*/
|
|
107
|
+
git?: GitSource | undefined;
|
|
93
108
|
/**
|
|
94
109
|
* Install from a platform catalog connector template (T26, filled in by the catalog service,
|
|
95
110
|
* not accepted over HTTP): the connector records its source item and version, the item policy is
|
|
@@ -139,6 +154,8 @@ export interface TemplateUpdateInput {
|
|
|
139
154
|
auth: ConnectorAuth;
|
|
140
155
|
/** The template version (itemId, version, templateId, defaultToolPolicies). */
|
|
141
156
|
template: ConnectorTemplateRef;
|
|
157
|
+
/** The new template's git hosts (template-owned: replaces `git_hosts`; absent clears them). */
|
|
158
|
+
git?: GitSource | undefined;
|
|
142
159
|
/**
|
|
143
160
|
* The auth method or the credentials it needs changed: the connector's credentials (org and
|
|
144
161
|
* personal, OAuth client and tokens) are cleared and `credential` (an org or the actor's static
|
|
@@ -194,6 +211,11 @@ export interface UpdateConnectorInput {
|
|
|
194
211
|
}> | undefined;
|
|
195
212
|
/** 把「没有 readOnlyHint / destructiveHint 注解」的工具默认策略批量改为 allow。 */
|
|
196
213
|
allowUnannotated?: boolean | undefined;
|
|
214
|
+
/**
|
|
215
|
+
* Use this connector's credential for git in sandboxes (sandbox git credentials spec §6.2): only
|
|
216
|
+
* an org-ownership connector that declares git hosts; pushes are then the bot's.
|
|
217
|
+
*/
|
|
218
|
+
useForGit?: boolean | undefined;
|
|
197
219
|
}
|
|
198
220
|
/** OAuth 安装准备的结果:要存的客户端,以及(客户端凭据流)安装时取到的组织 token。 */
|
|
199
221
|
export interface OAuthSetup {
|
|
@@ -207,7 +229,10 @@ export interface OAuthSetup {
|
|
|
207
229
|
export declare class ConnectorsService {
|
|
208
230
|
private readonly kdb;
|
|
209
231
|
private readonly registry;
|
|
210
|
-
/**
|
|
232
|
+
/**
|
|
233
|
+
* Extension manifests: a derived default slug avoids an extension's name (an explicit slug may
|
|
234
|
+
* take one; connector and extension tool keys live in separate namespaces).
|
|
235
|
+
*/
|
|
211
236
|
private readonly extensions;
|
|
212
237
|
private readonly credentials;
|
|
213
238
|
private readonly config;
|
|
@@ -218,7 +243,10 @@ export declare class ConnectorsService {
|
|
|
218
243
|
/** Setup guides of connector templates (spec 2026-11-09-setup-guides-v2-design.md §11). */
|
|
219
244
|
private readonly guides;
|
|
220
245
|
constructor(kdb: KernelDb, registry: ToolRegistry,
|
|
221
|
-
/**
|
|
246
|
+
/**
|
|
247
|
+
* Extension manifests: a derived default slug avoids an extension's name (an explicit slug may
|
|
248
|
+
* take one; connector and extension tool keys live in separate namespaces).
|
|
249
|
+
*/
|
|
222
250
|
extensions: Pick<ExtensionRegistry, "manifests">, credentials: Credentials, config: Pick<KernelConfigService, "get">, drift: ToolDrift,
|
|
223
251
|
/** The org row lock (orgs/org-lock.ts): joins the caller's transaction. */
|
|
224
252
|
orgLock: Pick<OrgLock, "lock" | "lockAndAuthorize">, txHost: TransactionHost,
|
|
@@ -344,6 +372,21 @@ export declare class ConnectorsService {
|
|
|
344
372
|
* global id); else `connector.not_found`.
|
|
345
373
|
*/
|
|
346
374
|
private liveOrgOf;
|
|
375
|
+
/**
|
|
376
|
+
* Disables a connector because its plugin was switched off in the org (spec §5 rule 8): its
|
|
377
|
+
* endpoint and credentials stay. Only a connector that was active is marked with the plugin
|
|
378
|
+
* (`connectors.disabled_by_plugin_id`), so switching the plugin back on re-enables exactly what
|
|
379
|
+
* this call disabled; one an org admin disabled by hand keeps no marker and stays disabled. The
|
|
380
|
+
* caller holds the org row lock and runs in its transaction.
|
|
381
|
+
*/
|
|
382
|
+
disableWithPluginIn(orgId: string, connectorId: string, itemId: string): Promise<void>;
|
|
383
|
+
/**
|
|
384
|
+
* Re-enables a connector the given plugin's switch-off disabled
|
|
385
|
+
* ({@link disableWithPluginIn}) when the plugin is switched back on and its install takes the
|
|
386
|
+
* connector back through the reuse path. A connector an org admin disabled by hand (no marker)
|
|
387
|
+
* or one another state left disabled stays disabled. Returns whether it switched it on.
|
|
388
|
+
*/
|
|
389
|
+
enableIfPluginDisabledIn(orgId: string, connectorId: string, itemId: string): Promise<boolean>;
|
|
347
390
|
/**
|
|
348
391
|
* Disables or enables a connector on the platform's behalf (a super admin who need not be a
|
|
349
392
|
* member of the org; the caller checks the super admin): org row lock, the status update, audit
|