@nilsr0711/drydock 0.1.8 → 0.1.9
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +4 -4
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/adrs/page.js +2 -2
- package/.next/standalone/.next/server/app/adrs/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/adrs/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/analytics/page.js +1 -1
- package/.next/standalone/.next/server/app/analytics/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/analytics/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/control/shutdown/route.js +1 -1
- package/.next/standalone/.next/server/app/api/control/shutdown/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cost/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/health/route.js +1 -1
- package/.next/standalone/.next/server/app/api/health/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sse/dashboard/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sse/jobs/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/webhooks/[repoId]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/webhooks/[repoId]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/costs/page.js +1 -1
- package/.next/standalone/.next/server/app/costs/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/costs/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/jobs/[id]/page.js +3 -3
- package/.next/standalone/.next/server/app/jobs/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/jobs/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/jobs/page.js +2 -2
- package/.next/standalone/.next/server/app/jobs/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/jobs/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/needs-human/page.js +2 -2
- package/.next/standalone/.next/server/app/needs-human/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/needs-human/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/prompts/page.js +2 -2
- package/.next/standalone/.next/server/app/prompts/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/prompts/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/repos/[id]/page.js +2 -2
- package/.next/standalone/.next/server/app/repos/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/repos/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page.js +2 -2
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +4 -4
- package/.next/standalone/.next/server/chunks/152.js +3 -0
- package/.next/standalone/.next/server/chunks/304.js +1 -1
- package/.next/standalone/.next/server/chunks/387.js +14 -14
- package/.next/standalone/.next/server/chunks/40.js +1 -1
- package/.next/standalone/.next/server/chunks/475.js +19 -0
- package/.next/standalone/.next/server/chunks/50.js +1 -0
- package/.next/standalone/.next/server/chunks/521.js +1 -1
- package/.next/standalone/.next/server/chunks/614.js +2 -2
- package/.next/standalone/.next/server/chunks/668.js +1 -0
- package/.next/standalone/.next/server/chunks/786.js +1 -1
- package/.next/standalone/.next/server/chunks/908.js +1 -1
- package/.next/standalone/.next/server/chunks/{685.js → 944.js} +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/6634-74f0b66587b57037.js +1 -0
- package/.next/standalone/.next/static/chunks/8382-5201fc3dd1f64e60.js +1 -0
- package/.next/standalone/.next/static/chunks/app/adrs/page-ca11e90e9404af90.js +1 -0
- package/.next/standalone/.next/static/chunks/app/jobs/[id]/page-dfb41b4785fd7cbb.js +1 -0
- package/.next/{static/chunks/app/layout-402d4ab34259b89b.js → standalone/.next/static/chunks/app/layout-134301ff0a9b828d.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/needs-human/page-569fc0e6aefc7677.js +1 -0
- package/.next/standalone/.next/static/chunks/app/page-504585be128c823c.js +1 -0
- package/.next/standalone/.next/static/chunks/app/prompts/page-c6bf6a4c782035ac.js +1 -0
- package/.next/standalone/.next/static/chunks/app/repos/[id]/page-c0c4531c2af46526.js +1 -0
- package/.next/standalone/.next/static/chunks/app/settings/page-84a44ab52a0ff49d.js +1 -0
- package/.next/standalone/drizzle/0036_adopt_claude_mem.sql +1 -0
- package/.next/standalone/mcp-server.cjs +304 -81
- package/.next/standalone/package.json +1 -1
- package/.next/static/chunks/6634-74f0b66587b57037.js +1 -0
- package/.next/static/chunks/8382-5201fc3dd1f64e60.js +1 -0
- package/.next/static/chunks/app/adrs/page-ca11e90e9404af90.js +1 -0
- package/.next/static/chunks/app/jobs/[id]/page-dfb41b4785fd7cbb.js +1 -0
- package/.next/{standalone/.next/static/chunks/app/layout-402d4ab34259b89b.js → static/chunks/app/layout-134301ff0a9b828d.js} +1 -1
- package/.next/static/chunks/app/needs-human/page-569fc0e6aefc7677.js +1 -0
- package/.next/static/chunks/app/page-504585be128c823c.js +1 -0
- package/.next/static/chunks/app/prompts/page-c6bf6a4c782035ac.js +1 -0
- package/.next/static/chunks/app/repos/[id]/page-c0c4531c2af46526.js +1 -0
- package/.next/static/chunks/app/settings/page-84a44ab52a0ff49d.js +1 -0
- package/README.md +11 -5
- package/drizzle/0036_adopt_claude_mem.sql +1 -0
- package/package.json +1 -1
- package/.next/standalone/.next/server/chunks/99.js +0 -21
- package/.next/standalone/.next/static/chunks/1298-0d60333e56ac8b2c.js +0 -1
- package/.next/standalone/.next/static/chunks/app/adrs/page-0c221fc18d1e89af.js +0 -1
- package/.next/standalone/.next/static/chunks/app/jobs/[id]/page-6ef8bc39b5181817.js +0 -1
- package/.next/standalone/.next/static/chunks/app/needs-human/page-b69ac22856c0a0b1.js +0 -1
- package/.next/standalone/.next/static/chunks/app/page-ccefa99d40734a8c.js +0 -1
- package/.next/standalone/.next/static/chunks/app/prompts/page-bcd1063eb5d64aa5.js +0 -1
- package/.next/standalone/.next/static/chunks/app/repos/[id]/page-2bd4b35a0b28042e.js +0 -1
- package/.next/standalone/.next/static/chunks/app/settings/page-13a04ecac139b74c.js +0 -1
- package/.next/static/chunks/1298-0d60333e56ac8b2c.js +0 -1
- package/.next/static/chunks/app/adrs/page-0c221fc18d1e89af.js +0 -1
- package/.next/static/chunks/app/jobs/[id]/page-6ef8bc39b5181817.js +0 -1
- package/.next/static/chunks/app/needs-human/page-b69ac22856c0a0b1.js +0 -1
- package/.next/static/chunks/app/page-ccefa99d40734a8c.js +0 -1
- package/.next/static/chunks/app/prompts/page-bcd1063eb5d64aa5.js +0 -1
- package/.next/static/chunks/app/repos/[id]/page-2bd4b35a0b28042e.js +0 -1
- package/.next/static/chunks/app/settings/page-13a04ecac139b74c.js +0 -1
- /package/.next/standalone/.next/static/{qSEvPCWIslg9lcxt6XQhl → gBXyOslZ8CoJrAVnOEHDm}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{qSEvPCWIslg9lcxt6XQhl → gBXyOslZ8CoJrAVnOEHDm}/_ssgManifest.js +0 -0
- /package/.next/static/{qSEvPCWIslg9lcxt6XQhl → gBXyOslZ8CoJrAVnOEHDm}/_buildManifest.js +0 -0
- /package/.next/static/{qSEvPCWIslg9lcxt6XQhl → gBXyOslZ8CoJrAVnOEHDm}/_ssgManifest.js +0 -0
|
@@ -8092,7 +8092,7 @@ var init_sql = __esm({
|
|
|
8092
8092
|
return new SQL([new StringChunk(str)]);
|
|
8093
8093
|
}
|
|
8094
8094
|
sql2.raw = raw;
|
|
8095
|
-
function
|
|
8095
|
+
function join18(chunks, separator) {
|
|
8096
8096
|
const result = [];
|
|
8097
8097
|
for (const [i, chunk] of chunks.entries()) {
|
|
8098
8098
|
if (i > 0 && separator !== void 0) {
|
|
@@ -8102,7 +8102,7 @@ var init_sql = __esm({
|
|
|
8102
8102
|
}
|
|
8103
8103
|
return new SQL(result);
|
|
8104
8104
|
}
|
|
8105
|
-
sql2.join =
|
|
8105
|
+
sql2.join = join18;
|
|
8106
8106
|
function identifier(value) {
|
|
8107
8107
|
return new Name(value);
|
|
8108
8108
|
}
|
|
@@ -11027,7 +11027,7 @@ var init_select2 = __esm({
|
|
|
11027
11027
|
const baseTableName = this.tableName;
|
|
11028
11028
|
const tableName = getTableLikeName(table);
|
|
11029
11029
|
for (const item of extractUsedTable(table)) this.usedTables.add(item);
|
|
11030
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
11030
|
+
if (typeof tableName === "string" && this.config.joins?.some((join18) => join18.alias === tableName)) {
|
|
11031
11031
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
11032
11032
|
}
|
|
11033
11033
|
if (!this.isPartialSelect) {
|
|
@@ -11917,7 +11917,7 @@ var init_update = __esm({
|
|
|
11917
11917
|
createJoin(joinType) {
|
|
11918
11918
|
return (table, on) => {
|
|
11919
11919
|
const tableName = getTableLikeName(table);
|
|
11920
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
11920
|
+
if (typeof tableName === "string" && this.config.joins.some((join18) => join18.alias === tableName)) {
|
|
11921
11921
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
11922
11922
|
}
|
|
11923
11923
|
if (typeof on === "function") {
|
|
@@ -13463,6 +13463,12 @@ var init_schema = __esm({
|
|
|
13463
13463
|
sandboxAllowNetwork: integer("sandbox_allow_network", { mode: "boolean" }).notNull().default(false),
|
|
13464
13464
|
sandboxCpus: text("sandbox_cpus"),
|
|
13465
13465
|
sandboxMemory: text("sandbox_memory"),
|
|
13466
|
+
// Opt-in claude-mem worktree adoption (issue #274, default off). When on, a
|
|
13467
|
+
// settling job triggers claude-mem's `adopt` for its worktree right before
|
|
13468
|
+
// Drydock removes it, consolidating the per-worktree memory into the parent
|
|
13469
|
+
// project while the worktree still exists. Best-effort and depends on the
|
|
13470
|
+
// external claude-mem plugin being installed, so it is off by default.
|
|
13471
|
+
adoptClaudeMem: integer("adopt_claude_mem", { mode: "boolean" }).notNull().default(false),
|
|
13466
13472
|
createdAt: integer("created_at").notNull().default(sql`(unixepoch())`)
|
|
13467
13473
|
});
|
|
13468
13474
|
promptTemplates = sqliteTable("prompt_templates", {
|
|
@@ -13498,6 +13504,11 @@ var init_schema = __esm({
|
|
|
13498
13504
|
// template (no saved repo version). Lets analytics slice outcomes by the
|
|
13499
13505
|
// exact prompt revision a job ran with, alongside model and agent.
|
|
13500
13506
|
implementPromptVersion: integer("implement_prompt_version"),
|
|
13507
|
+
// Per-job turn budget; 0 means unlimited (issue #254). The effective default
|
|
13508
|
+
// for new jobs comes from the global maxTurns setting (createJob always seeds
|
|
13509
|
+
// an explicit value), so this column default is dead for real inserts — it
|
|
13510
|
+
// stays at the frozen migration value (40) to avoid a no-op SQLite table
|
|
13511
|
+
// rebuild just to realign a fallback that is never hit.
|
|
13501
13512
|
maxTurns: integer("max_turns").notNull().default(40),
|
|
13502
13513
|
totalInputTokens: integer("total_input_tokens").notNull().default(0),
|
|
13503
13514
|
totalOutputTokens: integer("total_output_tokens").notNull().default(0),
|
|
@@ -13987,9 +13998,11 @@ var init_service = __esm({
|
|
|
13987
13998
|
// Per-job turn budget (issue #254). 0 is off (unlimited): the runner drops the
|
|
13988
13999
|
// CLI `--max-turns` flag and the OpenRouter loop skips its turn check, so a
|
|
13989
14000
|
// long task is bounded only by maxJobMinutes / the per-job cost cap. Defaults
|
|
13990
|
-
//
|
|
13991
|
-
//
|
|
13992
|
-
|
|
14001
|
+
// to 0 (unlimited) so a fresh install is fully autonomous out of the box —
|
|
14002
|
+
// ordinary issues routinely exceed any fixed turn wall and a max-turns abort
|
|
14003
|
+
// would otherwise escalate to needs_human. The value seeds each new job's
|
|
14004
|
+
// budget; set a positive ceiling here or per-call to cap turns.
|
|
14005
|
+
maxTurns: import_zod3.z.number().int().nonnegative().default(0),
|
|
13993
14006
|
// Hard wall-clock timeout per agent session in minutes (issue #47). A hung
|
|
13994
14007
|
// agent (network stall, MCP deadlock, stdin prompt) is aborted after this so
|
|
13995
14008
|
// it never holds a job slot forever. Defaults to 120 so long autonomous tasks
|
|
@@ -14015,6 +14028,15 @@ var init_service = __esm({
|
|
|
14015
14028
|
// Auto-wait on Codex usage limits (issue #167, ADR 030): the same park-and-
|
|
14016
14029
|
// resume treatment for OpenAI/ChatGPT-plan limits hit by the Codex CLI.
|
|
14017
14030
|
codexLimitAutoWait: import_zod3.z.boolean().default(true),
|
|
14031
|
+
// Auto-resume a job that exhausts its positive turn budget (issue #277). The
|
|
14032
|
+
// CLI aborts with an `error_max_turns` result and no provider-limit signal;
|
|
14033
|
+
// when on, Drydock resumes the stored session to continue the work (a bounded
|
|
14034
|
+
// number of times) instead of parking it in needs_human as "exited non-zero".
|
|
14035
|
+
// On by default per the autonomous model — a turn wall is recoverable, not an
|
|
14036
|
+
// operator decision. Off restores the plain escalation (with the clear
|
|
14037
|
+
// turn-budget reason either way). Only fires when a positive turn budget is
|
|
14038
|
+
// set; the default unlimited budget (0) never hits the wall.
|
|
14039
|
+
maxTurnsAutoResume: import_zod3.z.boolean().default(true),
|
|
14018
14040
|
// Global kill-switch for opt-in release management (issue #59, ADR 028). Off by
|
|
14019
14041
|
// default; both this and a repo's own `releaseEnabled` must be on for the
|
|
14020
14042
|
// release pipeline to run for that repo. Cutting a public release is hard to
|
|
@@ -26397,6 +26419,7 @@ function toParsed(event) {
|
|
|
26397
26419
|
} else {
|
|
26398
26420
|
base.sessionId = event.session_id;
|
|
26399
26421
|
base.resultText = event.result;
|
|
26422
|
+
base.resultSubtype = event.subtype;
|
|
26400
26423
|
base.costUsd = event.total_cost_usd;
|
|
26401
26424
|
base.inputTokens = event.usage?.input_tokens ?? 0;
|
|
26402
26425
|
base.outputTokens = event.usage?.output_tokens ?? 0;
|
|
@@ -26499,6 +26522,8 @@ var init_parser = __esm({
|
|
|
26499
26522
|
costUsd = 0;
|
|
26500
26523
|
/** Final result text from the stream's result event, when it carried one. */
|
|
26501
26524
|
resultText;
|
|
26525
|
+
/** Result event subtype from the stream, e.g. `error_max_turns` (issue #277). */
|
|
26526
|
+
resultSubtype;
|
|
26502
26527
|
/** Whether the stream's result event was flagged as an error (issue #166). */
|
|
26503
26528
|
resultIsError = false;
|
|
26504
26529
|
/** Latest subscription rate-limit snapshot seen in the stream (issue #188). */
|
|
@@ -26547,6 +26572,7 @@ var init_parser = __esm({
|
|
|
26547
26572
|
this.totalCacheReadInputTokens = parsed.cacheReadInputTokens;
|
|
26548
26573
|
if (parsed.costUsd !== void 0) this.costUsd = parsed.costUsd;
|
|
26549
26574
|
if (parsed.resultText !== void 0) this.resultText = parsed.resultText;
|
|
26575
|
+
if (parsed.resultSubtype !== void 0) this.resultSubtype = parsed.resultSubtype;
|
|
26550
26576
|
this.resultIsError = parsed.isError;
|
|
26551
26577
|
} else {
|
|
26552
26578
|
this.totalInputTokens += parsed.inputTokens;
|
|
@@ -28993,6 +29019,10 @@ function requireRepo(repoId, db) {
|
|
|
28993
29019
|
function listIssues(repoId, db = getDb()) {
|
|
28994
29020
|
return db.select().from(issues).where(eq(issues.repoId, repoId)).orderBy(asc(issues.priority), asc(issues.number)).all();
|
|
28995
29021
|
}
|
|
29022
|
+
function getIssueTitle(repoId, number2, db = getDb()) {
|
|
29023
|
+
const row = db.select({ title: issues.title }).from(issues).where(and(eq(issues.repoId, repoId), eq(issues.number, number2))).get();
|
|
29024
|
+
return row?.title ?? null;
|
|
29025
|
+
}
|
|
28996
29026
|
function syncIssuesFromGh(repoId, fetched, db = getDb()) {
|
|
28997
29027
|
const existing = db.select().from(issues).where(eq(issues.repoId, repoId)).all();
|
|
28998
29028
|
const existingByNumber = new Map(existing.map((i) => [i.number, i]));
|
|
@@ -30137,6 +30167,13 @@ function worktreeHome() {
|
|
|
30137
30167
|
function sanitize(name) {
|
|
30138
30168
|
return name.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
30139
30169
|
}
|
|
30170
|
+
function slugifyTitle(title) {
|
|
30171
|
+
return title.normalize("NFKD").replace(new RegExp("\\p{Diacritic}", "gu"), "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, MAX_SLUG_LENGTH).replace(/-+$/g, "");
|
|
30172
|
+
}
|
|
30173
|
+
function issueBranchLabel(issueNumber, title) {
|
|
30174
|
+
const slug = title ? slugifyTitle(title) : "";
|
|
30175
|
+
return slug ? `issue-${issueNumber}-${slug}` : `issue-${issueNumber}`;
|
|
30176
|
+
}
|
|
30140
30177
|
function repoWorktreesDir(repoName) {
|
|
30141
30178
|
return (0, import_node_path.join)(worktreeHome(), "worktrees", sanitize(repoName));
|
|
30142
30179
|
}
|
|
@@ -30147,7 +30184,7 @@ function stripAiAttribution(message) {
|
|
|
30147
30184
|
function nonEmptyCommitMessage(cleaned) {
|
|
30148
30185
|
return cleaned.trim() === "" ? STRIPPED_MESSAGE_FALLBACK : cleaned;
|
|
30149
30186
|
}
|
|
30150
|
-
var import_node_fs, import_node_os2, import_node_path, AI_ASSISTANT_NAMES, AI_ATTRIBUTION_LINE, STRIPPED_MESSAGE_FALLBACK, WorktreeError, EmptyCommitError, repoLocks, WorktreeManager;
|
|
30187
|
+
var import_node_fs, import_node_os2, import_node_path, MAX_SLUG_LENGTH, AI_ASSISTANT_NAMES, AI_ATTRIBUTION_LINE, STRIPPED_MESSAGE_FALLBACK, WorktreeError, EmptyCommitError, repoLocks, WorktreeManager;
|
|
30151
30188
|
var init_worktree = __esm({
|
|
30152
30189
|
"src/lib/git/worktree.ts"() {
|
|
30153
30190
|
"use strict";
|
|
@@ -30155,6 +30192,7 @@ var init_worktree = __esm({
|
|
|
30155
30192
|
import_node_os2 = require("node:os");
|
|
30156
30193
|
import_node_path = require("node:path");
|
|
30157
30194
|
init_runner();
|
|
30195
|
+
MAX_SLUG_LENGTH = 50;
|
|
30158
30196
|
AI_ASSISTANT_NAMES = "claude|anthropic|codex|openai|chatgpt|copilot|gemini|cursor|devin";
|
|
30159
30197
|
AI_ATTRIBUTION_LINE = new RegExp(
|
|
30160
30198
|
`^(?:co-authored-by:.*\\b(?:${AI_ASSISTANT_NAMES})\\b|\u{1F916}?\\s*generated (?:with|by)\\b.*\\b(?:${AI_ASSISTANT_NAMES})\\b)`,
|
|
@@ -30403,6 +30441,10 @@ var init_defaults = __esm({
|
|
|
30403
30441
|
ciFix: "ci-fix",
|
|
30404
30442
|
plan: "plan",
|
|
30405
30443
|
limitResume: "limit-resume",
|
|
30444
|
+
// Continuation prompt for a session resumed after exhausting its turn budget
|
|
30445
|
+
// (issue #277): unlike the limit-resume path, the worktree is kept intact, so
|
|
30446
|
+
// the prior session's uncommitted work is still present.
|
|
30447
|
+
turnResume: "turn-resume",
|
|
30406
30448
|
// Continuation prompt for a needs_human job an operator unblocked with typed
|
|
30407
30449
|
// guidance (issue #257); the human's instruction is injected via $INSTRUCTION.
|
|
30408
30450
|
humanResume: "human-resume",
|
|
@@ -30536,6 +30578,31 @@ var init_defaults = __esm({
|
|
|
30536
30578
|
"",
|
|
30537
30579
|
"Drydock appends `Closes #$ISSUE_NUM` and removes the file \u2014 do not commit it."
|
|
30538
30580
|
].join("\n"),
|
|
30581
|
+
// Continuation prompt for a session resumed after hitting its turn budget
|
|
30582
|
+
// (issue #277): the conversation context survives via --resume AND the worktree
|
|
30583
|
+
// is kept, so any uncommitted edits are still in place. The agent simply
|
|
30584
|
+
// continues where it left off rather than re-applying lost work.
|
|
30585
|
+
"turn-resume": [
|
|
30586
|
+
`Your previous session on issue #$ISSUE_NUM in "$REPO_NAME" was paused because it reached its`,
|
|
30587
|
+
`turn budget. You are resuming on branch "$BRANCH" with your conversation context and any`,
|
|
30588
|
+
"uncommitted edits from the interrupted session still in place \u2014 just continue from where you",
|
|
30589
|
+
"left off and finish implementing the issue. Follow the repo's conventions (`CLAUDE.md`/",
|
|
30590
|
+
"`AGENTS.md`, neighbouring code) and keep working test-first: a failing test that captures the",
|
|
30591
|
+
"requirement, then the code to make it green. Before you finish, verify: run the repo's tests,",
|
|
30592
|
+
"typecheck, lint, and build, and do not finish on a red signal. Never weaken or delete a test",
|
|
30593
|
+
"to make the suite pass. Keep the change focused. Split your work into focused, thematic",
|
|
30594
|
+
"commits, each with a clear Conventional Commit subject (`type(scope): summary`) grouped by",
|
|
30595
|
+
"concern \u2014 not one mega-commit. Never add AI attribution to a commit: no `Co-Authored-By`",
|
|
30596
|
+
"trailer naming an assistant, no `Generated with Claude Code` line, and no mention of the tool",
|
|
30597
|
+
"or model in the message. Do not push or open a pull request yourself; Drydock pushes and opens",
|
|
30598
|
+
"the PR, committing anything you leave uncommitted.",
|
|
30599
|
+
"Before finishing, write `.drydock/PR.md`: first line a Conventional Commit subject (used as",
|
|
30600
|
+
"the commit message and PR title), then a blank line, then a body in this format:",
|
|
30601
|
+
"",
|
|
30602
|
+
"$PR_FORMAT",
|
|
30603
|
+
"",
|
|
30604
|
+
"Drydock appends `Closes #$ISSUE_NUM` and removes the file \u2014 do not commit it."
|
|
30605
|
+
].join("\n"),
|
|
30539
30606
|
// Continuation prompt for a session resumed with human guidance (issue #257):
|
|
30540
30607
|
// a needs_human job an operator unblocked by typing how to proceed. The
|
|
30541
30608
|
// conversation context survives via --resume and the prior commits are
|
|
@@ -30999,7 +31066,10 @@ async function runOpenRouterJobSession(job, prompt, cwd, deps = {}) {
|
|
|
30999
31066
|
inputTokens: 0,
|
|
31000
31067
|
outputTokens: 0,
|
|
31001
31068
|
timedOut: false,
|
|
31002
|
-
costExceeded: false
|
|
31069
|
+
costExceeded: false,
|
|
31070
|
+
// The HTTP tool-loop enforces its own turn budget and never emits the CLI's
|
|
31071
|
+
// `error_max_turns` result, so it never sets this max-turns signal (issue #277).
|
|
31072
|
+
maxTurnsReached: false
|
|
31003
31073
|
};
|
|
31004
31074
|
const latch = agentLimitBlocked("openrouter", db);
|
|
31005
31075
|
if (latch) {
|
|
@@ -31192,7 +31262,16 @@ async function runOpenRouterJobSession(job, prompt, cwd, deps = {}) {
|
|
|
31192
31262
|
} else {
|
|
31193
31263
|
db.update(jobs).set({ totalInputTokens: inputTokens, totalOutputTokens: outputTokens, costUsd }).where(eq(jobs.id, job.id)).run();
|
|
31194
31264
|
}
|
|
31195
|
-
return {
|
|
31265
|
+
return {
|
|
31266
|
+
exitCode,
|
|
31267
|
+
costUsd,
|
|
31268
|
+
inputTokens,
|
|
31269
|
+
outputTokens,
|
|
31270
|
+
timedOut,
|
|
31271
|
+
costExceeded,
|
|
31272
|
+
maxTurnsReached: false,
|
|
31273
|
+
limit
|
|
31274
|
+
};
|
|
31196
31275
|
}
|
|
31197
31276
|
var TIMED_OUT_EXIT, COST_EXCEEDED_EXIT, LIMIT_BLOCKED_EXIT, SYSTEM_PROMPT;
|
|
31198
31277
|
var init_session3 = __esm({
|
|
@@ -31227,6 +31306,10 @@ var init_session3 = __esm({
|
|
|
31227
31306
|
});
|
|
31228
31307
|
|
|
31229
31308
|
// src/lib/orchestrator/agent-session.ts
|
|
31309
|
+
function reachedMaxTurns(parser, outcome) {
|
|
31310
|
+
if (outcome.timedOut || outcome.costExceeded) return false;
|
|
31311
|
+
return parser.resultSubtype === MAX_TURNS_RESULT_SUBTYPE;
|
|
31312
|
+
}
|
|
31230
31313
|
function limitGateResult(provider, job, broker, db) {
|
|
31231
31314
|
const latch = agentLimitBlocked(provider.id, db);
|
|
31232
31315
|
if (!latch) return void 0;
|
|
@@ -31243,6 +31326,7 @@ function limitGateResult(provider, job, broker, db) {
|
|
|
31243
31326
|
outputTokens: 0,
|
|
31244
31327
|
timedOut: false,
|
|
31245
31328
|
costExceeded: false,
|
|
31329
|
+
maxTurnsReached: false,
|
|
31246
31330
|
limit: {
|
|
31247
31331
|
agent: provider.id,
|
|
31248
31332
|
kind: latch.kind,
|
|
@@ -31466,6 +31550,7 @@ async function spawnAgentSession(job, prompt, cwd, deps = {}) {
|
|
|
31466
31550
|
outputTokens: parser.totalOutputTokens,
|
|
31467
31551
|
timedOut,
|
|
31468
31552
|
costExceeded,
|
|
31553
|
+
maxTurnsReached: reachedMaxTurns(parser, { timedOut, costExceeded }),
|
|
31469
31554
|
spawnError,
|
|
31470
31555
|
limit: classifySessionFailure(provider, parser, stderrTail, {
|
|
31471
31556
|
exitCode,
|
|
@@ -31595,6 +31680,7 @@ async function resumeAgentSession(job, sessionId, failedLog, cwd, deps = {}) {
|
|
|
31595
31680
|
outputTokens: parser.totalOutputTokens,
|
|
31596
31681
|
timedOut,
|
|
31597
31682
|
costExceeded,
|
|
31683
|
+
maxTurnsReached: reachedMaxTurns(parser, { timedOut, costExceeded }),
|
|
31598
31684
|
spawnError,
|
|
31599
31685
|
limit: classifySessionFailure(provider, parser, stderrTail, {
|
|
31600
31686
|
exitCode,
|
|
@@ -31603,7 +31689,7 @@ async function resumeAgentSession(job, sessionId, failedLog, cwd, deps = {}) {
|
|
|
31603
31689
|
})
|
|
31604
31690
|
};
|
|
31605
31691
|
}
|
|
31606
|
-
var TIMED_OUT_EXIT2, COST_EXCEEDED_EXIT2, LIMIT_BLOCKED_EXIT2, STDERR_TAIL_MAX, ZERO_USAGE;
|
|
31692
|
+
var MAX_TURNS_RESULT_SUBTYPE, TIMED_OUT_EXIT2, COST_EXCEEDED_EXIT2, LIMIT_BLOCKED_EXIT2, STDERR_TAIL_MAX, ZERO_USAGE;
|
|
31607
31693
|
var init_agent_session = __esm({
|
|
31608
31694
|
"src/lib/orchestrator/agent-session.ts"() {
|
|
31609
31695
|
"use strict";
|
|
@@ -31620,6 +31706,7 @@ var init_agent_session = __esm({
|
|
|
31620
31706
|
init_provider_limit();
|
|
31621
31707
|
init_provider_usage();
|
|
31622
31708
|
init_singleton();
|
|
31709
|
+
MAX_TURNS_RESULT_SUBTYPE = "error_max_turns";
|
|
31623
31710
|
TIMED_OUT_EXIT2 = -1;
|
|
31624
31711
|
COST_EXCEEDED_EXIT2 = -2;
|
|
31625
31712
|
LIMIT_BLOCKED_EXIT2 = -3;
|
|
@@ -34212,6 +34299,89 @@ var init_ci_babysitter = __esm({
|
|
|
34212
34299
|
}
|
|
34213
34300
|
});
|
|
34214
34301
|
|
|
34302
|
+
// src/lib/orchestrator/claude-mem-adopt.ts
|
|
34303
|
+
function defaultConfigDir() {
|
|
34304
|
+
return process.env.CLAUDE_CONFIG_DIR ?? (0, import_node_path8.join)((0, import_node_os5.homedir)(), ".claude");
|
|
34305
|
+
}
|
|
34306
|
+
function compareVersionDesc(a, b) {
|
|
34307
|
+
const pa = a.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
34308
|
+
const pb = b.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
34309
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
34310
|
+
const diff = (pb[i] ?? 0) - (pa[i] ?? 0);
|
|
34311
|
+
if (diff !== 0) return diff;
|
|
34312
|
+
}
|
|
34313
|
+
return b.localeCompare(a);
|
|
34314
|
+
}
|
|
34315
|
+
function normalizeRoot(root) {
|
|
34316
|
+
return (0, import_node_fs4.existsSync)((0, import_node_path8.join)(root, "plugin", "scripts")) ? (0, import_node_path8.join)(root, "plugin") : root;
|
|
34317
|
+
}
|
|
34318
|
+
function hasWorkerScripts(pluginDir) {
|
|
34319
|
+
return (0, import_node_fs4.existsSync)((0, import_node_path8.join)(pluginDir, "scripts", "bun-runner.js")) && (0, import_node_fs4.existsSync)((0, import_node_path8.join)(pluginDir, "scripts", "worker-service.cjs"));
|
|
34320
|
+
}
|
|
34321
|
+
function resolveClaudeMemPlugin(configDir = defaultConfigDir()) {
|
|
34322
|
+
const candidates = [];
|
|
34323
|
+
const envRoot = process.env.CLAUDE_PLUGIN_ROOT ?? process.env.PLUGIN_ROOT;
|
|
34324
|
+
if (envRoot) candidates.push(envRoot);
|
|
34325
|
+
const cacheDir = (0, import_node_path8.join)(configDir, "plugins", "cache", "thedotmack", "claude-mem");
|
|
34326
|
+
if ((0, import_node_fs4.existsSync)(cacheDir)) {
|
|
34327
|
+
let versions = [];
|
|
34328
|
+
try {
|
|
34329
|
+
versions = (0, import_node_fs4.readdirSync)(cacheDir, { withFileTypes: true }).filter((e) => e.isDirectory() && /^\d/.test(e.name)).map((e) => e.name).sort(compareVersionDesc);
|
|
34330
|
+
} catch {
|
|
34331
|
+
versions = [];
|
|
34332
|
+
}
|
|
34333
|
+
for (const version2 of versions) candidates.push((0, import_node_path8.join)(cacheDir, version2));
|
|
34334
|
+
}
|
|
34335
|
+
candidates.push((0, import_node_path8.join)(configDir, "plugins", "marketplaces", "thedotmack", "plugin"));
|
|
34336
|
+
for (const root of candidates) {
|
|
34337
|
+
const pluginDir = normalizeRoot(root);
|
|
34338
|
+
if (hasWorkerScripts(pluginDir)) return pluginDir;
|
|
34339
|
+
}
|
|
34340
|
+
return null;
|
|
34341
|
+
}
|
|
34342
|
+
async function adoptWorktreeMemory(input, opts = {}) {
|
|
34343
|
+
const resolvePlugin = opts.resolvePlugin ?? resolveClaudeMemPlugin;
|
|
34344
|
+
const run = opts.run ?? spawnRunner;
|
|
34345
|
+
try {
|
|
34346
|
+
const pluginDir = resolvePlugin(opts.configDir);
|
|
34347
|
+
if (!pluginDir) {
|
|
34348
|
+
logError(`[claude-mem] adoption skipped for ${input.branch}: plugin not installed`);
|
|
34349
|
+
return;
|
|
34350
|
+
}
|
|
34351
|
+
const args = [
|
|
34352
|
+
(0, import_node_path8.join)(pluginDir, "scripts", "bun-runner.js"),
|
|
34353
|
+
(0, import_node_path8.join)(pluginDir, "scripts", "worker-service.cjs"),
|
|
34354
|
+
"adopt",
|
|
34355
|
+
"--branch",
|
|
34356
|
+
input.branch,
|
|
34357
|
+
"--cwd",
|
|
34358
|
+
input.cwd
|
|
34359
|
+
];
|
|
34360
|
+
const result = await run("node", args, input.cwd, {
|
|
34361
|
+
timeoutMs: opts.timeoutMs ?? ADOPT_TIMEOUT_MS
|
|
34362
|
+
});
|
|
34363
|
+
if (result.exitCode !== 0) {
|
|
34364
|
+
logError(
|
|
34365
|
+
`[claude-mem] adoption for ${input.branch} exited ${result.exitCode}: ${result.stderr.trim()}`
|
|
34366
|
+
);
|
|
34367
|
+
}
|
|
34368
|
+
} catch (err) {
|
|
34369
|
+
logError(`[claude-mem] adoption failed for ${input.branch}`, err);
|
|
34370
|
+
}
|
|
34371
|
+
}
|
|
34372
|
+
var import_node_fs4, import_node_os5, import_node_path8, ADOPT_TIMEOUT_MS;
|
|
34373
|
+
var init_claude_mem_adopt = __esm({
|
|
34374
|
+
"src/lib/orchestrator/claude-mem-adopt.ts"() {
|
|
34375
|
+
"use strict";
|
|
34376
|
+
import_node_fs4 = require("node:fs");
|
|
34377
|
+
import_node_os5 = require("node:os");
|
|
34378
|
+
import_node_path8 = require("node:path");
|
|
34379
|
+
init_runner();
|
|
34380
|
+
init_logger2();
|
|
34381
|
+
ADOPT_TIMEOUT_MS = 6e4;
|
|
34382
|
+
}
|
|
34383
|
+
});
|
|
34384
|
+
|
|
34215
34385
|
// src/lib/orchestrator/followups-metadata.ts
|
|
34216
34386
|
function parseFollowups(raw) {
|
|
34217
34387
|
const entries = [];
|
|
@@ -34245,7 +34415,7 @@ function parseFollowups(raw) {
|
|
|
34245
34415
|
function readFollowups(worktreePath) {
|
|
34246
34416
|
let raw;
|
|
34247
34417
|
try {
|
|
34248
|
-
raw = (0,
|
|
34418
|
+
raw = (0, import_node_fs5.readFileSync)((0, import_node_path9.join)(worktreePath, FOLLOWUPS_METADATA_PATH), "utf8");
|
|
34249
34419
|
} catch {
|
|
34250
34420
|
return [];
|
|
34251
34421
|
}
|
|
@@ -34253,16 +34423,16 @@ function readFollowups(worktreePath) {
|
|
|
34253
34423
|
}
|
|
34254
34424
|
function consumeFollowups(worktreePath) {
|
|
34255
34425
|
const entries = readFollowups(worktreePath);
|
|
34256
|
-
(0,
|
|
34426
|
+
(0, import_node_fs5.rmSync)((0, import_node_path9.join)(worktreePath, FOLLOWUPS_METADATA_PATH), { force: true });
|
|
34257
34427
|
return entries;
|
|
34258
34428
|
}
|
|
34259
|
-
var
|
|
34429
|
+
var import_node_fs5, import_node_path9, FOLLOWUPS_METADATA_PATH, TITLE_MAX_CHARS, BODY_MAX_CHARS, MAX_ENTRIES, HEADING;
|
|
34260
34430
|
var init_followups_metadata = __esm({
|
|
34261
34431
|
"src/lib/orchestrator/followups-metadata.ts"() {
|
|
34262
34432
|
"use strict";
|
|
34263
|
-
|
|
34264
|
-
|
|
34265
|
-
FOLLOWUPS_METADATA_PATH = (0,
|
|
34433
|
+
import_node_fs5 = require("node:fs");
|
|
34434
|
+
import_node_path9 = require("node:path");
|
|
34435
|
+
FOLLOWUPS_METADATA_PATH = (0, import_node_path9.join)(".drydock", "FOLLOWUPS.md");
|
|
34266
34436
|
TITLE_MAX_CHARS = 300;
|
|
34267
34437
|
BODY_MAX_CHARS = 6e4;
|
|
34268
34438
|
MAX_ENTRIES = 20;
|
|
@@ -34657,7 +34827,7 @@ async function runPrAuditPass(deps) {
|
|
|
34657
34827
|
};
|
|
34658
34828
|
let generate = deps.generate;
|
|
34659
34829
|
if (!generate) {
|
|
34660
|
-
tmp = await (0, import_promises3.mkdtemp)((0,
|
|
34830
|
+
tmp = await (0, import_promises3.mkdtemp)((0, import_node_path10.join)((0, import_node_os6.tmpdir)(), "drydock-pr-audit-"));
|
|
34661
34831
|
const provider = getAgentProvider(config.agent);
|
|
34662
34832
|
generate = buildPrAuditGenerator({
|
|
34663
34833
|
provider,
|
|
@@ -34748,13 +34918,13 @@ function startPrAudit(jobId, db = getDb(), opts = {}) {
|
|
|
34748
34918
|
});
|
|
34749
34919
|
return { job, prNumber: job.prNumber, done };
|
|
34750
34920
|
}
|
|
34751
|
-
var import_promises3,
|
|
34921
|
+
var import_promises3, import_node_os6, import_node_path10, PR_AUDIT_TIMEOUT_MS;
|
|
34752
34922
|
var init_pr_audit_driver = __esm({
|
|
34753
34923
|
"src/lib/orchestrator/pr-audit-driver.ts"() {
|
|
34754
34924
|
"use strict";
|
|
34755
34925
|
import_promises3 = require("node:fs/promises");
|
|
34756
|
-
|
|
34757
|
-
|
|
34926
|
+
import_node_os6 = require("node:os");
|
|
34927
|
+
import_node_path10 = require("node:path");
|
|
34758
34928
|
init_registry();
|
|
34759
34929
|
init_types2();
|
|
34760
34930
|
init_client2();
|
|
@@ -34790,7 +34960,7 @@ function parsePrMetadata(raw) {
|
|
|
34790
34960
|
function readPrMetadata(worktreePath) {
|
|
34791
34961
|
let raw;
|
|
34792
34962
|
try {
|
|
34793
|
-
raw = (0,
|
|
34963
|
+
raw = (0, import_node_fs6.readFileSync)((0, import_node_path11.join)(worktreePath, PR_METADATA_PATH), "utf8");
|
|
34794
34964
|
} catch {
|
|
34795
34965
|
return null;
|
|
34796
34966
|
}
|
|
@@ -34798,16 +34968,16 @@ function readPrMetadata(worktreePath) {
|
|
|
34798
34968
|
}
|
|
34799
34969
|
function consumePrMetadata(worktreePath) {
|
|
34800
34970
|
const meta = readPrMetadata(worktreePath);
|
|
34801
|
-
(0,
|
|
34971
|
+
(0, import_node_fs6.rmSync)((0, import_node_path11.join)(worktreePath, PR_METADATA_PATH), { force: true });
|
|
34802
34972
|
return meta;
|
|
34803
34973
|
}
|
|
34804
|
-
var
|
|
34974
|
+
var import_node_fs6, import_node_path11, PR_METADATA_PATH, TITLE_MAX_CHARS2, BODY_MAX_CHARS2;
|
|
34805
34975
|
var init_pr_metadata = __esm({
|
|
34806
34976
|
"src/lib/orchestrator/pr-metadata.ts"() {
|
|
34807
34977
|
"use strict";
|
|
34808
|
-
|
|
34809
|
-
|
|
34810
|
-
PR_METADATA_PATH = (0,
|
|
34978
|
+
import_node_fs6 = require("node:fs");
|
|
34979
|
+
import_node_path11 = require("node:path");
|
|
34980
|
+
PR_METADATA_PATH = (0, import_node_path11.join)(".drydock", "PR.md");
|
|
34811
34981
|
TITLE_MAX_CHARS2 = 300;
|
|
34812
34982
|
BODY_MAX_CHARS2 = 6e4;
|
|
34813
34983
|
}
|
|
@@ -34855,7 +35025,7 @@ function parseQuestions(raw) {
|
|
|
34855
35025
|
function readQuestions(worktreePath) {
|
|
34856
35026
|
let raw;
|
|
34857
35027
|
try {
|
|
34858
|
-
raw = (0,
|
|
35028
|
+
raw = (0, import_node_fs7.readFileSync)((0, import_node_path12.join)(worktreePath, QUESTIONS_METADATA_PATH), "utf8");
|
|
34859
35029
|
} catch {
|
|
34860
35030
|
return null;
|
|
34861
35031
|
}
|
|
@@ -34863,16 +35033,16 @@ function readQuestions(worktreePath) {
|
|
|
34863
35033
|
}
|
|
34864
35034
|
function consumeQuestions(worktreePath) {
|
|
34865
35035
|
const questions = readQuestions(worktreePath);
|
|
34866
|
-
(0,
|
|
35036
|
+
(0, import_node_fs7.rmSync)((0, import_node_path12.join)(worktreePath, QUESTIONS_METADATA_PATH), { force: true });
|
|
34867
35037
|
return questions;
|
|
34868
35038
|
}
|
|
34869
|
-
var
|
|
35039
|
+
var import_node_fs7, import_node_path12, QUESTIONS_METADATA_PATH, QUESTIONS_MAX_CHARS;
|
|
34870
35040
|
var init_questions_metadata = __esm({
|
|
34871
35041
|
"src/lib/orchestrator/questions-metadata.ts"() {
|
|
34872
35042
|
"use strict";
|
|
34873
|
-
|
|
34874
|
-
|
|
34875
|
-
QUESTIONS_METADATA_PATH = (0,
|
|
35043
|
+
import_node_fs7 = require("node:fs");
|
|
35044
|
+
import_node_path12 = require("node:path");
|
|
35045
|
+
QUESTIONS_METADATA_PATH = (0, import_node_path12.join)(".drydock", "QUESTIONS.md");
|
|
34876
35046
|
QUESTIONS_MAX_CHARS = 6e4;
|
|
34877
35047
|
}
|
|
34878
35048
|
});
|
|
@@ -34905,7 +35075,7 @@ function parseReleaseMetadata(raw) {
|
|
|
34905
35075
|
function readReleaseMetadata(worktreePath) {
|
|
34906
35076
|
let raw;
|
|
34907
35077
|
try {
|
|
34908
|
-
raw = (0,
|
|
35078
|
+
raw = (0, import_node_fs8.readFileSync)((0, import_node_path13.join)(worktreePath, RELEASE_METADATA_PATH), "utf8");
|
|
34909
35079
|
} catch {
|
|
34910
35080
|
return null;
|
|
34911
35081
|
}
|
|
@@ -34913,16 +35083,16 @@ function readReleaseMetadata(worktreePath) {
|
|
|
34913
35083
|
}
|
|
34914
35084
|
function consumeReleaseMetadata(worktreePath) {
|
|
34915
35085
|
const meta = readReleaseMetadata(worktreePath);
|
|
34916
|
-
(0,
|
|
35086
|
+
(0, import_node_fs8.rmSync)((0, import_node_path13.join)(worktreePath, RELEASE_METADATA_PATH), { force: true });
|
|
34917
35087
|
return meta;
|
|
34918
35088
|
}
|
|
34919
|
-
var
|
|
35089
|
+
var import_node_fs8, import_node_path13, RELEASE_METADATA_PATH, TITLE_MAX_CHARS3, NOTES_MAX_CHARS, TAG_LINE, VERSION_TITLE;
|
|
34920
35090
|
var init_release_metadata = __esm({
|
|
34921
35091
|
"src/lib/orchestrator/release-metadata.ts"() {
|
|
34922
35092
|
"use strict";
|
|
34923
|
-
|
|
34924
|
-
|
|
34925
|
-
RELEASE_METADATA_PATH = (0,
|
|
35093
|
+
import_node_fs8 = require("node:fs");
|
|
35094
|
+
import_node_path13 = require("node:path");
|
|
35095
|
+
RELEASE_METADATA_PATH = (0, import_node_path13.join)(".drydock", "RELEASE.md");
|
|
34926
35096
|
TITLE_MAX_CHARS3 = 300;
|
|
34927
35097
|
NOTES_MAX_CHARS = 6e4;
|
|
34928
35098
|
TAG_LINE = /^tag:\s*(\S+)\s*$/i;
|
|
@@ -35287,7 +35457,7 @@ async function runVerificationPass(deps) {
|
|
|
35287
35457
|
};
|
|
35288
35458
|
let generate = deps.generate;
|
|
35289
35459
|
if (!generate) {
|
|
35290
|
-
tmp = await (0, import_promises4.mkdtemp)((0,
|
|
35460
|
+
tmp = await (0, import_promises4.mkdtemp)((0, import_node_path14.join)((0, import_node_os7.tmpdir)(), "drydock-verify-"));
|
|
35291
35461
|
generate = buildVerificationGenerator({
|
|
35292
35462
|
provider,
|
|
35293
35463
|
command,
|
|
@@ -35330,13 +35500,13 @@ async function runVerificationPass(deps) {
|
|
|
35330
35500
|
}
|
|
35331
35501
|
}
|
|
35332
35502
|
}
|
|
35333
|
-
var import_promises4,
|
|
35503
|
+
var import_promises4, import_node_os7, import_node_path14, VERIFY_TIMEOUT_MS, COMMENT_HEADER;
|
|
35334
35504
|
var init_verify_driver = __esm({
|
|
35335
35505
|
"src/lib/orchestrator/verify-driver.ts"() {
|
|
35336
35506
|
"use strict";
|
|
35337
35507
|
import_promises4 = require("node:fs/promises");
|
|
35338
|
-
|
|
35339
|
-
|
|
35508
|
+
import_node_os7 = require("node:os");
|
|
35509
|
+
import_node_path14 = require("node:path");
|
|
35340
35510
|
init_client2();
|
|
35341
35511
|
init_subtasks();
|
|
35342
35512
|
init_verify();
|
|
@@ -35521,6 +35691,7 @@ async function runJobCore(jobId, deps, send) {
|
|
|
35521
35691
|
const consumeQuestions2 = deps.consumeQuestions ?? consumeQuestions;
|
|
35522
35692
|
const consumeFollowups2 = deps.consumeFollowups ?? consumeFollowups;
|
|
35523
35693
|
const markNeedsHuman = deps.markNeedsHuman ?? ((issueNumber) => markIssueNeedsHuman(repo.id, issueNumber, db));
|
|
35694
|
+
const adoptClaudeMem = deps.adoptClaudeMem ?? adoptWorktreeMemory;
|
|
35524
35695
|
const resumeStoredSession = (j, prompt, cwd) => {
|
|
35525
35696
|
if (!j.sessionId) throw new Error(`job ${j.id} has no session id to resume`);
|
|
35526
35697
|
return resumeAgentSession(j, j.sessionId, "", cwd, {
|
|
@@ -35649,7 +35820,11 @@ async function runJobCore(jobId, deps, send) {
|
|
|
35649
35820
|
const humanInstruction = job.humanInstruction;
|
|
35650
35821
|
const instructionResume = !!humanInstruction && !!job.sessionId && provider.supportsResume;
|
|
35651
35822
|
const resumeOnExistingBranch = !!humanInstruction && !!job.branch;
|
|
35652
|
-
|
|
35823
|
+
const branchLabel = issueBranchLabel(
|
|
35824
|
+
job.issueNumber,
|
|
35825
|
+
getIssueTitle(repo.id, job.issueNumber, db)
|
|
35826
|
+
);
|
|
35827
|
+
wt = resumeOnExistingBranch ? await worktrees.prepareResume(repo, job.id, job.branch) : await worktrees.prepare(repo, job.id, job.issueNumber, branchLabel);
|
|
35653
35828
|
recordEvent(job.id, "worktree", { path: wt.path, branch: wt.branch }, db);
|
|
35654
35829
|
if (sandboxRequested) {
|
|
35655
35830
|
const prepared = await prepareSandbox({
|
|
@@ -35803,6 +35978,36 @@ ${planText}`
|
|
|
35803
35978
|
if (repo.autoDecompose) markSubtasksParked(repo.id, job.issueNumber, db);
|
|
35804
35979
|
return afterSession;
|
|
35805
35980
|
}
|
|
35981
|
+
let turnResumeAttempts = 0;
|
|
35982
|
+
while (session.maxTurnsReached && getSettings(db).maxTurnsAutoResume && session.sessionId && provider.supportsResume && turnResumeAttempts < MAX_TURN_RESUMES) {
|
|
35983
|
+
turnResumeAttempts += 1;
|
|
35984
|
+
recordEvent(
|
|
35985
|
+
job.id,
|
|
35986
|
+
"status",
|
|
35987
|
+
{
|
|
35988
|
+
reason: `turn budget (${job.maxTurns}) reached, resuming`,
|
|
35989
|
+
attempt: turnResumeAttempts,
|
|
35990
|
+
sessionId: session.sessionId
|
|
35991
|
+
},
|
|
35992
|
+
db
|
|
35993
|
+
);
|
|
35994
|
+
if (repo.autoDecompose) markSubtasksWorking(repo.id, job.issueNumber, db);
|
|
35995
|
+
const resumePrompt = renderTemplate(
|
|
35996
|
+
resolveTemplateContent(repo.id, TEMPLATE_NAMES.turnResume, db),
|
|
35997
|
+
{
|
|
35998
|
+
ISSUE_NUM: job.issueNumber,
|
|
35999
|
+
BRANCH: wt.branch,
|
|
36000
|
+
REPO_NAME: repo.name,
|
|
36001
|
+
PR_FORMAT: resolveTemplateContent(repo.id, TEMPLATE_NAMES.prFormat, db)
|
|
36002
|
+
}
|
|
36003
|
+
);
|
|
36004
|
+
session = await resumeLimitSession(getJob(job.id, db), resumePrompt, wt.path);
|
|
36005
|
+
const afterResume = getJob(job.id, db);
|
|
36006
|
+
if (afterResume.status === "aborted" || afterResume.status === "interrupted") {
|
|
36007
|
+
if (repo.autoDecompose) markSubtasksParked(repo.id, job.issueNumber, db);
|
|
36008
|
+
return afterResume;
|
|
36009
|
+
}
|
|
36010
|
+
}
|
|
35806
36011
|
if (session.timedOut) {
|
|
35807
36012
|
return await parkForHuman(`${provider.label} timed out after ${maxJobMinutes} minutes`);
|
|
35808
36013
|
}
|
|
@@ -35825,6 +36030,9 @@ ${planText}`
|
|
|
35825
36030
|
}
|
|
35826
36031
|
return await parkOnLimit(limit);
|
|
35827
36032
|
}
|
|
36033
|
+
if (session.maxTurnsReached) {
|
|
36034
|
+
return await parkForHuman(`turn budget (${job.maxTurns}) reached`);
|
|
36035
|
+
}
|
|
35828
36036
|
if (session.exitCode !== 0) {
|
|
35829
36037
|
return await parkForHuman(`${provider.label} exited non-zero`);
|
|
35830
36038
|
}
|
|
@@ -35942,6 +36150,13 @@ ${questions}`
|
|
|
35942
36150
|
return current;
|
|
35943
36151
|
} finally {
|
|
35944
36152
|
if (wt && !preserveWorktree) {
|
|
36153
|
+
if (repo.adoptClaudeMem) {
|
|
36154
|
+
try {
|
|
36155
|
+
await adoptClaudeMem({ branch: wt.branch, cwd: wt.path });
|
|
36156
|
+
} catch (adoptErr) {
|
|
36157
|
+
logError(`[run-job] claude-mem adoption failed for job ${job.id}`, adoptErr);
|
|
36158
|
+
}
|
|
36159
|
+
}
|
|
35945
36160
|
try {
|
|
35946
36161
|
await worktrees.remove(wt, repo.path);
|
|
35947
36162
|
} catch (cleanupErr) {
|
|
@@ -35950,7 +36165,7 @@ ${questions}`
|
|
|
35950
36165
|
}
|
|
35951
36166
|
}
|
|
35952
36167
|
}
|
|
35953
|
-
var PLAN_MAX_CHARS, ISSUE_TITLE_MAX_CHARS, ISSUE_BODY_MAX_CHARS, HUMAN_INSTRUCTION_MAX_CHARS;
|
|
36168
|
+
var MAX_TURN_RESUMES, PLAN_MAX_CHARS, ISSUE_TITLE_MAX_CHARS, ISSUE_BODY_MAX_CHARS, HUMAN_INSTRUCTION_MAX_CHARS;
|
|
35954
36169
|
var init_run_job = __esm({
|
|
35955
36170
|
"src/lib/orchestrator/run-job.ts"() {
|
|
35956
36171
|
"use strict";
|
|
@@ -35975,6 +36190,7 @@ var init_run_job = __esm({
|
|
|
35975
36190
|
init_agent_command();
|
|
35976
36191
|
init_agent_session();
|
|
35977
36192
|
init_ci_babysitter();
|
|
36193
|
+
init_claude_mem_adopt();
|
|
35978
36194
|
init_followups_metadata();
|
|
35979
36195
|
init_jobs();
|
|
35980
36196
|
init_needs_human();
|
|
@@ -35988,6 +36204,7 @@ var init_run_job = __esm({
|
|
|
35988
36204
|
init_state_machine();
|
|
35989
36205
|
init_subtask_driver();
|
|
35990
36206
|
init_verify_driver();
|
|
36207
|
+
MAX_TURN_RESUMES = 3;
|
|
35991
36208
|
PLAN_MAX_CHARS = 1e4;
|
|
35992
36209
|
ISSUE_TITLE_MAX_CHARS = 500;
|
|
35993
36210
|
ISSUE_BODY_MAX_CHARS = 2e4;
|
|
@@ -36022,7 +36239,7 @@ function waitForIdle(timeoutMs = 3e4, pollMs = 100) {
|
|
|
36022
36239
|
});
|
|
36023
36240
|
}
|
|
36024
36241
|
function lockPath() {
|
|
36025
|
-
return (0,
|
|
36242
|
+
return (0, import_node_path15.join)(worktreeHome(), "instance.lock");
|
|
36026
36243
|
}
|
|
36027
36244
|
function pidAlive(pid) {
|
|
36028
36245
|
try {
|
|
@@ -36042,17 +36259,17 @@ function parseLock(text2) {
|
|
|
36042
36259
|
}
|
|
36043
36260
|
function readLock(path2) {
|
|
36044
36261
|
try {
|
|
36045
|
-
return parseLock((0,
|
|
36262
|
+
return parseLock((0, import_node_fs9.readFileSync)(path2, "utf8"));
|
|
36046
36263
|
} catch {
|
|
36047
36264
|
return { pid: null, ts: null };
|
|
36048
36265
|
}
|
|
36049
36266
|
}
|
|
36050
36267
|
function writeLock(path2) {
|
|
36051
|
-
const fd = (0,
|
|
36268
|
+
const fd = (0, import_node_fs9.openSync)(path2, "wx");
|
|
36052
36269
|
try {
|
|
36053
|
-
(0,
|
|
36270
|
+
(0, import_node_fs9.writeSync)(fd, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
36054
36271
|
} finally {
|
|
36055
|
-
(0,
|
|
36272
|
+
(0, import_node_fs9.closeSync)(fd);
|
|
36056
36273
|
}
|
|
36057
36274
|
}
|
|
36058
36275
|
function lockIsStale(record2, now) {
|
|
@@ -36064,7 +36281,7 @@ function lockIsStale(record2, now) {
|
|
|
36064
36281
|
}
|
|
36065
36282
|
function acquireInstanceLock() {
|
|
36066
36283
|
const path2 = lockPath();
|
|
36067
|
-
(0,
|
|
36284
|
+
(0, import_node_fs9.mkdirSync)((0, import_node_path15.dirname)(path2), { recursive: true });
|
|
36068
36285
|
try {
|
|
36069
36286
|
writeLock(path2);
|
|
36070
36287
|
return true;
|
|
@@ -36072,7 +36289,7 @@ function acquireInstanceLock() {
|
|
|
36072
36289
|
}
|
|
36073
36290
|
if (!lockIsStale(readLock(path2), Date.now())) return false;
|
|
36074
36291
|
try {
|
|
36075
|
-
(0,
|
|
36292
|
+
(0, import_node_fs9.unlinkSync)(path2);
|
|
36076
36293
|
writeLock(path2);
|
|
36077
36294
|
return true;
|
|
36078
36295
|
} catch {
|
|
@@ -36084,16 +36301,16 @@ function refreshInstanceLock() {
|
|
|
36084
36301
|
if (readLock(path2).pid !== process.pid) return false;
|
|
36085
36302
|
const tmp = `${path2}.${process.pid}.tmp`;
|
|
36086
36303
|
try {
|
|
36087
|
-
(0,
|
|
36304
|
+
(0, import_node_fs9.writeFileSync)(tmp, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
36088
36305
|
if (readLock(path2).pid !== process.pid) {
|
|
36089
|
-
(0,
|
|
36306
|
+
(0, import_node_fs9.unlinkSync)(tmp);
|
|
36090
36307
|
return false;
|
|
36091
36308
|
}
|
|
36092
|
-
(0,
|
|
36309
|
+
(0, import_node_fs9.renameSync)(tmp, path2);
|
|
36093
36310
|
return true;
|
|
36094
36311
|
} catch {
|
|
36095
36312
|
try {
|
|
36096
|
-
(0,
|
|
36313
|
+
(0, import_node_fs9.unlinkSync)(tmp);
|
|
36097
36314
|
} catch {
|
|
36098
36315
|
}
|
|
36099
36316
|
return false;
|
|
@@ -36117,16 +36334,16 @@ function releaseInstanceLock() {
|
|
|
36117
36334
|
const path2 = lockPath();
|
|
36118
36335
|
if (readLock(path2).pid !== process.pid) return;
|
|
36119
36336
|
try {
|
|
36120
|
-
(0,
|
|
36337
|
+
(0, import_node_fs9.unlinkSync)(path2);
|
|
36121
36338
|
} catch {
|
|
36122
36339
|
}
|
|
36123
36340
|
}
|
|
36124
|
-
var
|
|
36341
|
+
var import_node_fs9, import_node_path15, draining, activeJobs, LOCK_HEARTBEAT_MS, LOCK_TTL_MS, heartbeatTimer;
|
|
36125
36342
|
var init_runtime2 = __esm({
|
|
36126
36343
|
"src/lib/orchestrator/runtime.ts"() {
|
|
36127
36344
|
"use strict";
|
|
36128
|
-
|
|
36129
|
-
|
|
36345
|
+
import_node_fs9 = require("node:fs");
|
|
36346
|
+
import_node_path15 = require("node:path");
|
|
36130
36347
|
init_worktree();
|
|
36131
36348
|
draining = false;
|
|
36132
36349
|
activeJobs = /* @__PURE__ */ new Set();
|
|
@@ -36514,7 +36731,7 @@ async function reapOrphanedWorktrees(deps = {}) {
|
|
|
36514
36731
|
const dir = repoWorktreesDir(repo.name);
|
|
36515
36732
|
let entries;
|
|
36516
36733
|
try {
|
|
36517
|
-
entries = (0,
|
|
36734
|
+
entries = (0, import_node_fs10.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
36518
36735
|
} catch {
|
|
36519
36736
|
continue;
|
|
36520
36737
|
}
|
|
@@ -36524,12 +36741,12 @@ async function reapOrphanedWorktrees(deps = {}) {
|
|
|
36524
36741
|
const jobId = Number(match[1]);
|
|
36525
36742
|
if (live.has(jobId)) continue;
|
|
36526
36743
|
if (isLiveJob(jobId, db)) continue;
|
|
36527
|
-
const path2 = (0,
|
|
36744
|
+
const path2 = (0, import_node_path16.join)(dir, entry);
|
|
36528
36745
|
await run("git", ["-C", repo.path, "worktree", "remove", "--force", path2]).catch(
|
|
36529
36746
|
() => void 0
|
|
36530
36747
|
);
|
|
36531
36748
|
try {
|
|
36532
|
-
(0,
|
|
36749
|
+
(0, import_node_fs10.rmSync)(path2, { recursive: true, force: true });
|
|
36533
36750
|
reaped++;
|
|
36534
36751
|
} catch (err) {
|
|
36535
36752
|
logError(`[worktree-reaper] failed to remove ${path2}`, err);
|
|
@@ -36538,12 +36755,12 @@ async function reapOrphanedWorktrees(deps = {}) {
|
|
|
36538
36755
|
}
|
|
36539
36756
|
return reaped;
|
|
36540
36757
|
}
|
|
36541
|
-
var
|
|
36758
|
+
var import_node_fs10, import_node_path16, JOB_DIR, FB_DIR, DH_DIR;
|
|
36542
36759
|
var init_worktree_reaper = __esm({
|
|
36543
36760
|
"src/lib/orchestrator/worktree-reaper.ts"() {
|
|
36544
36761
|
"use strict";
|
|
36545
|
-
|
|
36546
|
-
|
|
36762
|
+
import_node_fs10 = require("node:fs");
|
|
36763
|
+
import_node_path16 = require("node:path");
|
|
36547
36764
|
init_drizzle_orm();
|
|
36548
36765
|
init_client2();
|
|
36549
36766
|
init_queries();
|
|
@@ -36666,7 +36883,7 @@ function startOrchestrator() {
|
|
|
36666
36883
|
process.once("SIGINT", onSignal);
|
|
36667
36884
|
process.once("SIGTERM", onSignal);
|
|
36668
36885
|
}
|
|
36669
|
-
var started, abortHandles, DEFAULT_ABORT_GRACE_MS, IDLE_WAIT_MS, PRUNE_INTERVAL_MS;
|
|
36886
|
+
var started, ABORT_HANDLES_KEY, globalWithAbort, abortHandles, DEFAULT_ABORT_GRACE_MS, IDLE_WAIT_MS, PRUNE_INTERVAL_MS;
|
|
36670
36887
|
var init_singleton = __esm({
|
|
36671
36888
|
"src/lib/orchestrator/singleton.ts"() {
|
|
36672
36889
|
"use strict";
|
|
@@ -36682,7 +36899,10 @@ var init_singleton = __esm({
|
|
|
36682
36899
|
init_runtime2();
|
|
36683
36900
|
init_worktree_reaper();
|
|
36684
36901
|
started = false;
|
|
36685
|
-
|
|
36902
|
+
ABORT_HANDLES_KEY = Symbol.for("drydock.orchestrator.abort-handles");
|
|
36903
|
+
globalWithAbort = globalThis;
|
|
36904
|
+
globalWithAbort[ABORT_HANDLES_KEY] ??= /* @__PURE__ */ new Map();
|
|
36905
|
+
abortHandles = globalWithAbort[ABORT_HANDLES_KEY];
|
|
36686
36906
|
DEFAULT_ABORT_GRACE_MS = 5e3;
|
|
36687
36907
|
IDLE_WAIT_MS = DEFAULT_ABORT_GRACE_MS + 3e3;
|
|
36688
36908
|
PRUNE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -36691,13 +36911,13 @@ var init_singleton = __esm({
|
|
|
36691
36911
|
|
|
36692
36912
|
// src/lib/db/client.ts
|
|
36693
36913
|
function resolveMigrationsDir() {
|
|
36694
|
-
return process.env.DRYDOCK_MIGRATIONS ?? (0,
|
|
36914
|
+
return process.env.DRYDOCK_MIGRATIONS ?? (0, import_node_path17.resolve)(process.cwd(), "drizzle");
|
|
36695
36915
|
}
|
|
36696
36916
|
function applyMigrations(sqlite) {
|
|
36697
36917
|
const migrationsFolder = resolveMigrationsDir();
|
|
36698
36918
|
let files;
|
|
36699
36919
|
try {
|
|
36700
|
-
files = (0,
|
|
36920
|
+
files = (0, import_node_fs11.readdirSync)(migrationsFolder).filter((f) => f.endsWith(".sql")).sort();
|
|
36701
36921
|
} catch {
|
|
36702
36922
|
return;
|
|
36703
36923
|
}
|
|
@@ -36708,7 +36928,7 @@ function applyMigrations(sqlite) {
|
|
|
36708
36928
|
const record2 = sqlite.prepare("INSERT INTO __migrations (name) VALUES (?)");
|
|
36709
36929
|
for (const file of files) {
|
|
36710
36930
|
if (applied.has(file)) continue;
|
|
36711
|
-
const sql2 = (0,
|
|
36931
|
+
const sql2 = (0, import_node_fs11.readFileSync)((0, import_node_path17.join)(migrationsFolder, file), "utf8");
|
|
36712
36932
|
sqlite.pragma("foreign_keys = OFF");
|
|
36713
36933
|
try {
|
|
36714
36934
|
const run = sqlite.transaction(() => {
|
|
@@ -36733,7 +36953,7 @@ function applyMigrations(sqlite) {
|
|
|
36733
36953
|
}
|
|
36734
36954
|
function createDb(dbPath) {
|
|
36735
36955
|
if (dbPath !== ":memory:") {
|
|
36736
|
-
(0,
|
|
36956
|
+
(0, import_node_fs11.mkdirSync)((0, import_node_path17.dirname)(dbPath), { recursive: true });
|
|
36737
36957
|
}
|
|
36738
36958
|
const sqlite = new import_better_sqlite32.default(dbPath);
|
|
36739
36959
|
try {
|
|
@@ -36749,7 +36969,7 @@ function createDb(dbPath) {
|
|
|
36749
36969
|
function getDb() {
|
|
36750
36970
|
if (singletonError) throw singletonError;
|
|
36751
36971
|
if (!singleton2) {
|
|
36752
|
-
const path2 = process.env.DRYDOCK_DB ?? (0,
|
|
36972
|
+
const path2 = process.env.DRYDOCK_DB ?? (0, import_node_path17.resolve)(process.cwd(), "data/drydock.db");
|
|
36753
36973
|
try {
|
|
36754
36974
|
singleton2 = createDb(path2);
|
|
36755
36975
|
} catch (err) {
|
|
@@ -36760,12 +36980,12 @@ function getDb() {
|
|
|
36760
36980
|
}
|
|
36761
36981
|
return singleton2;
|
|
36762
36982
|
}
|
|
36763
|
-
var
|
|
36983
|
+
var import_node_fs11, import_node_path17, import_better_sqlite32, FOREIGN_KEYS_PRAGMA, singleton2, singletonError;
|
|
36764
36984
|
var init_client2 = __esm({
|
|
36765
36985
|
"src/lib/db/client.ts"() {
|
|
36766
36986
|
"use strict";
|
|
36767
|
-
|
|
36768
|
-
|
|
36987
|
+
import_node_fs11 = require("node:fs");
|
|
36988
|
+
import_node_path17 = require("node:path");
|
|
36769
36989
|
import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
|
|
36770
36990
|
init_better_sqlite3();
|
|
36771
36991
|
init_logger2();
|
|
@@ -42496,15 +42716,15 @@ function resumeJobWithInstruction(jobId, instruction, db = getDb()) {
|
|
|
42496
42716
|
}
|
|
42497
42717
|
|
|
42498
42718
|
// src/lib/repos/path.ts
|
|
42499
|
-
var
|
|
42500
|
-
var
|
|
42719
|
+
var import_node_fs12 = require("node:fs");
|
|
42720
|
+
var import_node_path18 = require("node:path");
|
|
42501
42721
|
function isGitRepoPath(path2) {
|
|
42502
42722
|
try {
|
|
42503
|
-
if (!(0,
|
|
42723
|
+
if (!(0, import_node_fs12.statSync)(path2).isDirectory()) return false;
|
|
42504
42724
|
} catch {
|
|
42505
42725
|
return false;
|
|
42506
42726
|
}
|
|
42507
|
-
return (0,
|
|
42727
|
+
return (0, import_node_fs12.existsSync)((0, import_node_path18.join)(path2, ".git"));
|
|
42508
42728
|
}
|
|
42509
42729
|
|
|
42510
42730
|
// src/lib/repos/service.ts
|
|
@@ -42617,7 +42837,10 @@ var repoInputSchema = import_zod18.z.object({
|
|
|
42617
42837
|
sandboxImage: import_zod18.z.string().nullish(),
|
|
42618
42838
|
sandboxAllowNetwork: import_zod18.z.boolean().default(false),
|
|
42619
42839
|
sandboxCpus: import_zod18.z.string().nullish(),
|
|
42620
|
-
sandboxMemory: import_zod18.z.string().nullish()
|
|
42840
|
+
sandboxMemory: import_zod18.z.string().nullish(),
|
|
42841
|
+
// Opt-in claude-mem worktree adoption (issue #274). Off by default: it depends
|
|
42842
|
+
// on the external claude-mem plugin being installed. See the schema column.
|
|
42843
|
+
adoptClaudeMem: import_zod18.z.boolean().default(false)
|
|
42621
42844
|
});
|
|
42622
42845
|
function assertModelAllowedForAgent(agent, model, db) {
|
|
42623
42846
|
if (agent === "openrouter") {
|