@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.
Files changed (88) hide show
  1. package/dist/app.js +2 -0
  2. package/dist/catalog/catalog-content.d.ts +18 -1
  3. package/dist/catalog/catalog-content.js +5 -2
  4. package/dist/catalog/catalog-scope.service.js +3 -2
  5. package/dist/catalog/marketplaces.service.d.ts +1 -1
  6. package/dist/catalog/org-catalog.service.js +13 -3
  7. package/dist/catalog/plugin-install.d.ts +37 -16
  8. package/dist/catalog/plugin-install.js +108 -34
  9. package/dist/catalog/plugin-upgrade.js +1 -0
  10. package/dist/channels/actions.d.ts +4 -1
  11. package/dist/channels/actions.js +5 -2
  12. package/dist/channels/cards.d.ts +7 -1
  13. package/dist/channels/cards.js +28 -1
  14. package/dist/channels/phrases.d.ts +15 -0
  15. package/dist/channels/phrases.js +9 -0
  16. package/dist/config/schema.d.ts +8 -0
  17. package/dist/config/schema.js +69 -0
  18. package/dist/conversations/events.d.ts +14 -0
  19. package/dist/conversations/events.js +20 -0
  20. package/dist/db/kernel-schema.d.ts +19 -4
  21. package/dist/errors.d.ts +5 -0
  22. package/dist/errors.js +5 -0
  23. package/dist/git/gateway.d.ts +93 -0
  24. package/dist/git/gateway.js +750 -0
  25. package/dist/git/gateway.route.d.ts +16 -0
  26. package/dist/git/gateway.route.js +41 -0
  27. package/dist/git/git.module.d.ts +10 -0
  28. package/dist/git/git.module.js +31 -0
  29. package/dist/git/identity.d.ts +80 -0
  30. package/dist/git/identity.js +209 -0
  31. package/dist/git/pkt-line.d.ts +53 -0
  32. package/dist/git/pkt-line.js +146 -0
  33. package/dist/git/run-end.d.ts +29 -0
  34. package/dist/git/run-end.js +121 -0
  35. package/dist/git/sources.d.ts +42 -0
  36. package/dist/git/sources.js +134 -0
  37. package/dist/git/tickets.d.ts +69 -0
  38. package/dist/git/tickets.js +194 -0
  39. package/dist/http/api.module.js +3 -0
  40. package/dist/http/app.d.ts +6 -0
  41. package/dist/http/app.js +3 -0
  42. package/dist/infra/config/kernel-config.service.js +2 -0
  43. package/dist/orgs/settings.service.d.ts +21 -0
  44. package/dist/orgs/settings.service.js +18 -0
  45. package/dist/overrides.d.ts +6 -0
  46. package/dist/overrides.js +4 -0
  47. package/dist/platform/exclusions.js +3 -0
  48. package/dist/runtime/run-finish.d.ts +5 -0
  49. package/dist/runtime/run-finish.js +12 -3
  50. package/dist/runtime/runtime.module.js +2 -0
  51. package/dist/runtime/waits.js +7 -5
  52. package/dist/schedules/fire.d.ts +3 -3
  53. package/dist/schedules/fire.js +7 -4
  54. package/dist/schedules/grant-rules.d.ts +40 -0
  55. package/dist/schedules/grant-rules.js +83 -0
  56. package/dist/schedules/scheduled-tasks.service.d.ts +9 -7
  57. package/dist/schedules/scheduled-tasks.service.js +50 -15
  58. package/dist/schedules/types.d.ts +2 -0
  59. package/dist/tools/approvals.service.d.ts +100 -19
  60. package/dist/tools/approvals.service.js +288 -21
  61. package/dist/tools/builtin-names.d.ts +4 -2
  62. package/dist/tools/builtin-names.js +6 -1
  63. package/dist/tools/builtins/schedules.d.ts +11 -9
  64. package/dist/tools/builtins/schedules.js +202 -44
  65. package/dist/tools/connector-account-policies.d.ts +8 -0
  66. package/dist/tools/connector-account-policies.js +8 -0
  67. package/dist/tools/connectors.controller.d.ts +2 -0
  68. package/dist/tools/connectors.controller.js +5 -1
  69. package/dist/tools/connectors.service.d.ts +46 -3
  70. package/dist/tools/connectors.service.js +98 -9
  71. package/dist/tools/credentials.d.ts +18 -0
  72. package/dist/tools/credentials.js +60 -0
  73. package/dist/tools/credentials.module.d.ts +7 -0
  74. package/dist/tools/credentials.module.js +22 -0
  75. package/dist/tools/git-asks.d.ts +113 -0
  76. package/dist/tools/git-asks.js +174 -0
  77. package/dist/tools/git-push-policy.d.ts +89 -0
  78. package/dist/tools/git-push-policy.js +175 -0
  79. package/dist/tools/info.d.ts +3 -3
  80. package/dist/tools/info.js +3 -3
  81. package/dist/tools/network-asks.d.ts +5 -0
  82. package/dist/tools/network-asks.js +9 -2
  83. package/dist/tools/tools.module.js +4 -3
  84. package/package.json +3 -3
  85. package/src/migrations/0126_git_gateway.sql +10 -0
  86. package/src/migrations/0127_git_push_policy.sql +12 -0
  87. package/src/migrations/0128_scheduled_task_plugin_disabled.sql +10 -0
  88. package/src/migrations/0129_connector_plugin_disabled.sql +5 -0
@@ -0,0 +1,89 @@
1
+ import type { KernelSchemaDb } from "../db/kernel-schema.js";
2
+ import type { KernelConfigService } from "../infra/config/kernel-config.service.js";
3
+ import { type UnattendedRun } from "../schedules/unattended.js";
4
+ import { type GitRunState } from "./git-asks.js";
5
+ import type { ToolPolicy } from "./registry.js";
6
+ /** Why {@link decideGitPush} decided as it did. */
7
+ export type GitPushReason =
8
+ /** The org's default for the connector allows pushes. */
9
+ "org_allow"
10
+ /** The member's standing allow for the repository. */
11
+ | "member_allow"
12
+ /** An approved 「这次允许」 of the run tree for the repository. */
13
+ | "run_grant"
14
+ /** The scheduled task's `git:push` / `git:push:<owner>/<repo>` grant (unattended run). */
15
+ | "task_grant"
16
+ /** The policy asks the actor. */
17
+ | "ask"
18
+ /** The org's default denies pushes through the connector. */
19
+ | "org_deny"
20
+ /** The member denied pushes to the repository for themselves. */
21
+ | "member_deny"
22
+ /** The org forbids `git:push` (or the repository's key) in scheduled runs. */
23
+ | "forbidden"
24
+ /** An unattended run in `fail` mode without a task grant: no one can be asked. */
25
+ | "unattended"
26
+ /** The connector is not a live, active connector of the org. */
27
+ | "unavailable"
28
+ /** The repository is not `<owner>/<repo>`. */
29
+ | "invalid_repo";
30
+ /** The decision about one push. */
31
+ export interface GitPushDecision {
32
+ decision: "allow" | "ask" | "deny";
33
+ reason: GitPushReason;
34
+ /** The repository as keyed (`<owner>/<repo>`, lower-cased); null when it was invalid. */
35
+ repo: string | null;
36
+ /** An unattended refusal's `missing_grants` key (`git:push:<owner>/<repo>`) and task run. */
37
+ missingGrant?: {
38
+ taskRunId: string;
39
+ key: string;
40
+ };
41
+ }
42
+ /** What {@link decideGitPush} decides about. */
43
+ export interface GitPushInput {
44
+ orgId: string;
45
+ /** The actor whose credential the push uses (the ticket's account). */
46
+ accountId: string;
47
+ /** The run the exec belongs to (the ticket's run). */
48
+ runId: string;
49
+ /** The git source the push uses (the connector whose credential is injected). */
50
+ connectorId: string;
51
+ host: string;
52
+ /** `<owner>/<repo>` (a trailing `.git` and case are normalized here). */
53
+ repo: string;
54
+ /**
55
+ * The scheduled task context of the run, when the caller has it (`unattendedRunOf`); undefined:
56
+ * read here. Null: an attended run.
57
+ */
58
+ unattended?: UnattendedRun | null;
59
+ }
60
+ /** A decision the actor's or the org's policy settles without grants (pure). */
61
+ export declare function gitPushBase(input: {
62
+ orgDefault: ToolPolicy;
63
+ /** The member's own setting for the repository (a personal connector only). */
64
+ member?: ToolPolicy | undefined;
65
+ }): {
66
+ policy: ToolPolicy;
67
+ reason: GitPushReason;
68
+ };
69
+ /**
70
+ * Decides a push (sandbox git credentials spec §7), highest first:
71
+ * 1. the org's default `deny` for the connector; the member's own `deny` for the repository;
72
+ * 2. an unattended run (no trial, no person joined) whose org forbids `git:push` or the
73
+ * repository's key in scheduled runs (scheduled tasks spec §7): deny, whatever allows it below;
74
+ * 3. the member's standing allow for the repository on the host (personal connector), else the
75
+ * org's default `allow`; a member's `ask` keeps an org `allow` asking;
76
+ * 4. an ask is allowed by a run grant (an approved `once` git ask of the live run tree for the
77
+ * host, repository and connector, decided for the same actor);
78
+ * 5. an unattended run: a task grant `git:push` or `git:push:<owner>/<repo>` allows; else `fail`
79
+ * mode refuses (`unattended`, with the missing grant to record) and `wait` mode asks the
80
+ * creator.
81
+ * Grants never lift a deny. The caller records the missing grant and the Redis ask record.
82
+ */
83
+ export declare function decideGitPush(db: KernelSchemaDb, config: Pick<KernelConfigService, "get">, input: GitPushInput): Promise<GitPushDecision>;
84
+ /**
85
+ * The run tree's git asks (the network asks' rules, network ask-to-allow spec §6.8): the
86
+ * repositories granted and refused in the tree (keyed `<host>/<owner>/<repo>`, {@link gitRepoKey}),
87
+ * this run's asks, a pending ask of the tree.
88
+ */
89
+ export declare function gitRunState(db: KernelSchemaDb, runId: string): Promise<GitRunState>;
@@ -0,0 +1,175 @@
1
+ import { sql } from "kysely";
2
+ import { budgetRunOf } from "../runtime/subagents/limits.js";
3
+ import { GIT_PUSH_GRANT, gitPushForbidden, gitPushGrantKey, normalizeGitRepo, } from "../schedules/grant-rules.js";
4
+ import { unattendedRules, unattendedRunOf } from "../schedules/unattended.js";
5
+ import { gitPushPolicyName, personalAuth } from "./connector-account-policies.js";
6
+ import { gitRepoKey } from "./git-asks.js";
7
+ import { connectorPolicy } from "./policy.js";
8
+ /**
9
+ * The push policy of the git gateway (sandbox git credentials spec §7): fetches run, a push
10
+ * follows the connector's virtual tool `git_push`. Read from PG on every push (no in-process
11
+ * state), so a decision on one replica holds on every other.
12
+ *
13
+ * Storage (no new table): the org's default is `connectors.git_push_policy` (default `ask`); a
14
+ * member's standing allow (or deny) for one repository is their `connector_tool_account_policies`
15
+ * row `git_push:<host>/<owner>/<repo>` (a personal connector only: an org connector's push acts as the
16
+ * org); a run grant is an approved `once` git ask of the run tree; a scheduled task's grants are
17
+ * `git:push` and `git:push:<owner>/<repo>` in `scheduled_task_grants`.
18
+ */
19
+ /** The statuses of a run that has not ended: its run grants still apply. */
20
+ const LIVE_RUN = ["queued", "running", "waiting"];
21
+ /** The statuses of a git ask that refuse its repository for the rest of the run tree. */
22
+ const REFUSING = new Set(["denied", "expired", "skipped", "cancelled"]);
23
+ /** A decision the actor's or the org's policy settles without grants (pure). */
24
+ export function gitPushBase(input) {
25
+ const r = connectorPolicy({ orgDefault: input.orgDefault, member: input.member });
26
+ if (r.policy === "deny")
27
+ return { policy: "deny", reason: r.source === "org" ? "org_deny" : "member_deny" };
28
+ if (r.policy === "allow") {
29
+ return { policy: "allow", reason: r.source === "member" ? "member_allow" : "org_allow" };
30
+ }
31
+ return { policy: "ask", reason: "ask" };
32
+ }
33
+ /**
34
+ * Decides a push (sandbox git credentials spec §7), highest first:
35
+ * 1. the org's default `deny` for the connector; the member's own `deny` for the repository;
36
+ * 2. an unattended run (no trial, no person joined) whose org forbids `git:push` or the
37
+ * repository's key in scheduled runs (scheduled tasks spec §7): deny, whatever allows it below;
38
+ * 3. the member's standing allow for the repository on the host (personal connector), else the
39
+ * org's default `allow`; a member's `ask` keeps an org `allow` asking;
40
+ * 4. an ask is allowed by a run grant (an approved `once` git ask of the live run tree for the
41
+ * host, repository and connector, decided for the same actor);
42
+ * 5. an unattended run: a task grant `git:push` or `git:push:<owner>/<repo>` allows; else `fail`
43
+ * mode refuses (`unattended`, with the missing grant to record) and `wait` mode asks the
44
+ * creator.
45
+ * Grants never lift a deny. The caller records the missing grant and the Redis ask record.
46
+ */
47
+ export async function decideGitPush(db, config, input) {
48
+ const repo = normalizeGitRepo(input.repo);
49
+ if (repo === null)
50
+ return { decision: "deny", reason: "invalid_repo", repo: null };
51
+ const host = input.host.toLowerCase();
52
+ const connector = await db
53
+ .selectFrom("connectors")
54
+ .select(["auth", "status", "git_push_policy"])
55
+ .where("id", "=", input.connectorId)
56
+ .where("org_id", "=", input.orgId)
57
+ .where("deleted_at", "is", null)
58
+ .executeTakeFirst();
59
+ if (connector?.status !== "active") {
60
+ return { decision: "deny", reason: "unavailable", repo };
61
+ }
62
+ let member;
63
+ if (personalAuth(connector.auth)) {
64
+ const row = await db
65
+ .selectFrom("connector_tool_account_policies")
66
+ .select("policy")
67
+ .where("connector_id", "=", input.connectorId)
68
+ .where("account_id", "=", input.accountId)
69
+ .where("tool_name", "=", gitPushPolicyName(host, repo))
70
+ .executeTakeFirst();
71
+ member = row?.policy;
72
+ }
73
+ const base = gitPushBase({ orgDefault: connector.git_push_policy, member });
74
+ if (base.policy === "deny")
75
+ return { decision: "deny", reason: base.reason, repo };
76
+ // what an unattended run may not do at all comes before every allow (as the tool policy)
77
+ const key = gitPushGrantKey(repo);
78
+ const unattended = input.unattended !== undefined
79
+ ? input.unattended
80
+ : await unattendedRunOf(db, config, input.orgId, input.runId);
81
+ const rules = unattendedRules(unattended) ? unattended : null;
82
+ if (rules !== null && gitPushForbidden(rules.forbiddenTools, key)) {
83
+ return { decision: "deny", reason: "forbidden", repo };
84
+ }
85
+ if (base.policy === "allow")
86
+ return { decision: "allow", reason: base.reason, repo };
87
+ if (await hasRunGrant(db, input, host, repo)) {
88
+ return { decision: "allow", reason: "run_grant", repo };
89
+ }
90
+ if (rules !== null) {
91
+ const granted = await db
92
+ .selectFrom("scheduled_task_grants")
93
+ .select("tool_key")
94
+ .where("task_id", "=", rules.taskId)
95
+ .where("tool_key", "in", [GIT_PUSH_GRANT, key])
96
+ .executeTakeFirst();
97
+ if (granted !== undefined)
98
+ return { decision: "allow", reason: "task_grant", repo };
99
+ if (rules.unattendedAsk === "fail") {
100
+ return {
101
+ decision: "deny",
102
+ reason: "unattended",
103
+ repo,
104
+ missingGrant: { taskRunId: rules.taskRunId, key },
105
+ };
106
+ }
107
+ }
108
+ return { decision: "ask", reason: "ask", repo };
109
+ }
110
+ /** The runs of the tree whose budget run is `root`. */
111
+ const treeOf = (db, root) => db
112
+ .selectFrom("runs")
113
+ .select("id")
114
+ .where((eb) => eb.or([eb("id", "=", root), eb("budget_run_id", "=", root)]));
115
+ /**
116
+ * Whether the run tree holds a run grant for this push: an approved `once` git ask of the tree in
117
+ * the org about the same host, repository and connector, asked of the same actor, while the tree's
118
+ * root run has not ended (a grant ends with its run; a later run of the same budget window does
119
+ * not inherit it). An `always` decision is the member's policy row and is read there, not here.
120
+ */
121
+ async function hasRunGrant(db, input, host, repo) {
122
+ const root = await budgetRunOf(db, input.runId);
123
+ const live = await db
124
+ .selectFrom("runs")
125
+ .select("id")
126
+ .where("id", "=", root)
127
+ .where("status", "in", LIVE_RUN)
128
+ .executeTakeFirst();
129
+ if (live === undefined)
130
+ return false;
131
+ const row = await db
132
+ .selectFrom("approvals")
133
+ .select("id")
134
+ .where("kind", "=", "git")
135
+ .where("org_id", "=", input.orgId)
136
+ .where("connector_id", "=", input.connectorId)
137
+ .where("requested_for", "=", input.accountId)
138
+ .where("status", "=", "approved")
139
+ .where("scope", "=", "once")
140
+ .where(sql `detail->>'repo'`, "=", repo)
141
+ .where(sql `lower(detail->>'host')`, "=", host)
142
+ .where("run_id", "in", treeOf(db, root))
143
+ .executeTakeFirst();
144
+ return row !== undefined;
145
+ }
146
+ /**
147
+ * The run tree's git asks (the network asks' rules, network ask-to-allow spec §6.8): the
148
+ * repositories granted and refused in the tree (keyed `<host>/<owner>/<repo>`, {@link gitRepoKey}),
149
+ * this run's asks, a pending ask of the tree.
150
+ */
151
+ export async function gitRunState(db, runId) {
152
+ const root = await budgetRunOf(db, runId);
153
+ const rows = await db
154
+ .selectFrom("approvals")
155
+ .select(["run_id", "status", "scope", "detail"])
156
+ .where("kind", "=", "git")
157
+ .where("run_id", "in", treeOf(db, root))
158
+ .execute();
159
+ const state = { granted: new Set(), refused: new Set(), asks: 0, pending: false };
160
+ for (const r of rows) {
161
+ if (r.run_id === runId)
162
+ state.asks++;
163
+ const d = r.detail;
164
+ if (r.status === "pending")
165
+ state.pending = true;
166
+ else if (typeof d?.repo !== "string" || typeof d.host !== "string")
167
+ continue;
168
+ else if (r.status === "approved" && r.scope === "once") {
169
+ state.granted.add(gitRepoKey(d.host, d.repo));
170
+ }
171
+ else if (REFUSING.has(r.status))
172
+ state.refused.add(gitRepoKey(d.host, d.repo));
173
+ }
174
+ return state;
175
+ }
@@ -69,9 +69,9 @@ export interface ToolInfoDeps {
69
69
  * - Extension tools resolve deployment-wide (from the loaded host), not per org: a tool of an
70
70
  * extension that is not enabled in this org still answers `source: extension` with its title.
71
71
  * That is deliberate: old conversation logs keep their labels, and the data is not sensitive.
72
- * - A key whose prefix is both a loaded extension's name and a connector slug resolves to the
73
- * extension. When an agent's tools are built, the registry's `claim()` reports that collision;
74
- * here it only changes the label.
72
+ * - Extension and connector keys never compete for one key: extension tools are
73
+ * `plugin__<plugin>__<tool>` and connector tools `mcp__<slug>__<tool>`, so an extension name and
74
+ * a connector slug may be the same.
75
75
  */
76
76
  export declare function toolInfo(deps: ToolInfoDeps, orgId: string, keys: readonly string[]): Promise<ToolInfo[]>;
77
77
  /** `GET /conversations/:id/tools`: {@link toolInfo} for a conversation the viewer can read. */
@@ -39,9 +39,9 @@ const display = (spec) => ({
39
39
  * - Extension tools resolve deployment-wide (from the loaded host), not per org: a tool of an
40
40
  * extension that is not enabled in this org still answers `source: extension` with its title.
41
41
  * That is deliberate: old conversation logs keep their labels, and the data is not sensitive.
42
- * - A key whose prefix is both a loaded extension's name and a connector slug resolves to the
43
- * extension. When an agent's tools are built, the registry's `claim()` reports that collision;
44
- * here it only changes the label.
42
+ * - Extension and connector keys never compete for one key: extension tools are
43
+ * `plugin__<plugin>__<tool>` and connector tools `mcp__<slug>__<tool>`, so an extension name and
44
+ * a connector slug may be the same.
45
45
  */
46
46
  export async function toolInfo(deps, orgId, keys) {
47
47
  const unique = [...new Set(keys)];
@@ -107,3 +107,8 @@ export declare const withNetworkNotes: (text: string, notes: readonly string[])
107
107
  * {@link NETWORK_ASK_CLIPPED_NOTE}. The caller archives large output first, so this rarely cuts.
108
108
  */
109
109
  export declare function clipKept(content: string): string;
110
+ /**
111
+ * `content` within {@link NETWORK_ASK_KEPT_MAX_BYTES} (UTF-8): unchanged when it fits, else its
112
+ * head and then `note` on a line of its own (the git asks clip with their own note).
113
+ */
114
+ export declare function clipKeptWith(content: string, note: string): string;
@@ -210,13 +210,20 @@ export const withNetworkNotes = (text, notes) => notes.length === 0 ? text : `${
210
210
  * {@link NETWORK_ASK_CLIPPED_NOTE}. The caller archives large output first, so this rarely cuts.
211
211
  */
212
212
  export function clipKept(content) {
213
+ return clipKeptWith(content, NETWORK_ASK_CLIPPED_NOTE);
214
+ }
215
+ /**
216
+ * `content` within {@link NETWORK_ASK_KEPT_MAX_BYTES} (UTF-8): unchanged when it fits, else its
217
+ * head and then `note` on a line of its own (the git asks clip with their own note).
218
+ */
219
+ export function clipKeptWith(content, note) {
213
220
  const bytes = Buffer.from(content, "utf8");
214
221
  if (bytes.length <= NETWORK_ASK_KEPT_MAX_BYTES)
215
222
  return content;
216
- const room = NETWORK_ASK_KEPT_MAX_BYTES - Buffer.byteLength(NETWORK_ASK_CLIPPED_NOTE) - 1;
223
+ const room = NETWORK_ASK_KEPT_MAX_BYTES - Buffer.byteLength(note) - 1;
217
224
  // a cut inside a multi-byte character drops it rather than keeping half of it
218
225
  const head = new TextDecoder("utf-8", { fatal: false })
219
226
  .decode(bytes.subarray(0, room))
220
227
  .replace(/�$/, "");
221
- return `${head}\n${NETWORK_ASK_CLIPPED_NOTE}`;
228
+ return `${head}\n${note}`;
222
229
  }
@@ -33,7 +33,7 @@ import { WebPages } from "./builtins/web/page-cache.js";
33
33
  import { ConnectorAccountPolicies } from "./connector-account-policies.js";
34
34
  import { ConnectorContent } from "./connector-content.js";
35
35
  import { ConnectorsService } from "./connectors.service.js";
36
- import { Credentials } from "./credentials.js";
36
+ import { CredentialsModule } from "./credentials.module.js";
37
37
  import { DeviceOpResolver } from "./device-op-resolver.js";
38
38
  import { ToolDrift } from "./drift.js";
39
39
  import { ConversationToolInfo, OrgExtensionTools } from "./info.js";
@@ -83,9 +83,10 @@ ToolsModule = __decorate([
83
83
  QueueModule.registerQueue(MAINTENANCE_QUEUE_NAME),
84
84
  // a trial run's approvals become its scheduled task's grants (scheduled tasks spec §7)
85
85
  ScheduledTaskStoreModule,
86
+ // connector credentials (also the git gateway's, without importing this module)
87
+ CredentialsModule,
86
88
  ],
87
89
  providers: [
88
- Credentials,
89
90
  ToolOutputs,
90
91
  BuiltinTools,
91
92
  // `ScheduleUpdate`'s write in one transaction (scheduled tasks spec §11)
@@ -112,7 +113,7 @@ ToolsModule = __decorate([
112
113
  ],
113
114
  exports: [
114
115
  ScheduleToolsPort,
115
- Credentials,
116
+ CredentialsModule,
116
117
  ToolOutputs,
117
118
  BuiltinTools,
118
119
  ToolRegistry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capybara-hq/kernel",
3
- "version": "0.1.0-rc.26",
3
+ "version": "0.1.0-rc.28",
4
4
  "description": "Capybara kernel: config, database, plugin host and HTTP API of the Capybara agent platform",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -65,7 +65,7 @@
65
65
  "yaml": "^2.9.1",
66
66
  "yauzl": "3.4.0",
67
67
  "zod": "^4.6.7",
68
- "@capybara-hq/plugin-sdk": "^0.1.0-rc.20"
68
+ "@capybara-hq/plugin-sdk": "^0.1.0-rc.21"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@langchain/langgraph-checkpoint-validation": "^1.2.0",
@@ -74,7 +74,7 @@
74
74
  "@types/ws": "^8.18.2",
75
75
  "@types/yauzl": "3.4.0",
76
76
  "ws": "^8.22.0",
77
- "@capybara-hq/testing": "0.1.0-rc.17"
77
+ "@capybara-hq/testing": "0.1.0-rc.18"
78
78
  },
79
79
  "scripts": {
80
80
  "build": "tsc -b"
@@ -0,0 +1,10 @@
1
+ -- Git credentials for sandboxes (spec 2026-11-12-sandbox-git-credentials-design.md): a connector
2
+ -- may declare the git hosts it can serve, and an org-owned one may be used as the source of the
3
+ -- git gateway (personal connectors are always sources). `git_hosts` is null for connectors that
4
+ -- declare none.
5
+ ALTER TABLE connectors ADD COLUMN IF NOT EXISTS git_hosts jsonb;
6
+ ALTER TABLE connectors ADD COLUMN IF NOT EXISTS use_for_git boolean NOT NULL DEFAULT false;
7
+
8
+ -- An approval of kind 'git' asks for a git operation through the gateway (widening 0114's CHECK).
9
+ ALTER TABLE approvals DROP CONSTRAINT IF EXISTS approvals_kind_check;
10
+ ALTER TABLE approvals ADD CONSTRAINT approvals_kind_check CHECK (kind IN ('tool', 'network', 'git'));
@@ -0,0 +1,12 @@
1
+ -- The org's push policy for a connector's git hosts (sandbox git credentials spec §7): the
2
+ -- connector's virtual tool `git_push` (`allow` / `ask` / `deny`, default `ask`). It has no row in
3
+ -- `connector_tools` (that table mirrors the tools the connector serves, kept in step by drift
4
+ -- review), so it is the connector's own column. A member's standing allow for one repository is a
5
+ -- `connector_tool_account_policies` row named `git_push:<host>/<owner>/<repo>` (no schema change).
6
+ ALTER TABLE connectors ADD COLUMN IF NOT EXISTS git_push_policy text NOT NULL DEFAULT 'ask';
7
+ ALTER TABLE connectors DROP CONSTRAINT IF EXISTS connectors_git_push_policy_check;
8
+ ALTER TABLE connectors ADD CONSTRAINT connectors_git_push_policy_check
9
+ CHECK (git_push_policy IN ('allow', 'ask', 'deny'));
10
+
11
+ -- Run grants and the per-run caps read the git asks of a run tree.
12
+ CREATE INDEX IF NOT EXISTS approvals_git_run_idx ON approvals (run_id) WHERE kind = 'git';
@@ -0,0 +1,10 @@
1
+ -- A chat task whose channel extension is disabled in the org skips its runs with `plugin_disabled`
2
+ -- (scheduled tasks spec §11), distinct from `target_gone`: the task stays active and recovers when
3
+ -- the extension is enabled again. Widening the inline CHECK of 0113 (PG's default name
4
+ -- scheduled_task_runs_skip_reason_check): drop and add in the file's one transaction, idempotent.
5
+ ALTER TABLE scheduled_task_runs DROP CONSTRAINT IF EXISTS scheduled_task_runs_skip_reason_check;
6
+ ALTER TABLE scheduled_task_runs ADD CONSTRAINT scheduled_task_runs_skip_reason_check
7
+ CHECK (skip_reason IN ('missed', 'overlap', 'rate_limited', 'queue_paused',
8
+ 'removed', 'device_offline', 'creator_inactive', 'agent_gone', 'target_gone',
9
+ 'conversation_gone', 'org_disabled', 'unsupported_target', 'task_deleted', 'task_paused',
10
+ 'plugin_disabled'));
@@ -0,0 +1,5 @@
1
+ -- A connector's plugin was switched off in its org: the connector is kept with its endpoint and
2
+ -- credentials but disabled, and marked with the plugin (the catalog item) that switched it off, so
3
+ -- switching the plugin back on re-enables exactly the connectors it disabled. A connector an org
4
+ -- admin disabled by hand has no marker (null) and stays disabled.
5
+ ALTER TABLE connectors ADD COLUMN IF NOT EXISTS disabled_by_plugin_id uuid;