opencode-magi 0.5.0 → 0.6.1
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/config/resolve.js +3 -3
- package/dist/config/validate.js +31 -20
- package/dist/config/worktree.js +6 -0
- package/dist/github/commands.js +197 -65
- package/dist/index.js +29 -3
- package/dist/orchestrator/ci.js +32 -21
- package/dist/orchestrator/findings.js +29 -7
- package/dist/orchestrator/majority.js +1 -1
- package/dist/orchestrator/merge.js +31 -10
- package/dist/orchestrator/model.js +23 -9
- package/dist/orchestrator/review.js +162 -36
- package/dist/orchestrator/run-manager.js +171 -146
- package/dist/orchestrator/triage.js +246 -204
- package/dist/prompts/compose.js +2 -42
- package/dist/prompts/contracts.js +6 -20
- package/dist/prompts/output.js +6 -16
- package/dist/prompts/templates/triage/existing-pr.md +1 -1
- package/package.json +1 -1
- package/schema.json +3 -5
- package/dist/prompts/templates/triage/action.md +0 -5
- package/dist/prompts/templates/triage/comment.md +0 -5
- package/dist/prompts/templates/triage/question.md +0 -5
package/dist/orchestrator/ci.js
CHANGED
|
@@ -176,7 +176,15 @@ function ciFailureContextForClassified(items, classified) {
|
|
|
176
176
|
return "";
|
|
177
177
|
return [
|
|
178
178
|
"CI has scope-in failures that may be caused by this PR.",
|
|
179
|
-
"
|
|
179
|
+
"Treat these failures as blocking review issues until the checks pass.",
|
|
180
|
+
[
|
|
181
|
+
"Do not approve this PR while this ci_failure_context is present.",
|
|
182
|
+
"Return CHANGES_REQUESTED and include a finding for each failing CI check.",
|
|
183
|
+
].join(" "),
|
|
184
|
+
[
|
|
185
|
+
"Still inspect the PR diff before reporting findings.",
|
|
186
|
+
"If a CI failure does not map to an exact changed line, anchor the finding to the nearest responsible or first relevant changed line.",
|
|
187
|
+
].join(" "),
|
|
180
188
|
"",
|
|
181
189
|
...sections,
|
|
182
190
|
].join("\n\n");
|
|
@@ -221,7 +229,7 @@ async function watchRerunRuns(exec, repository, checks) {
|
|
|
221
229
|
await Promise.all(runIds.map((runId) => watchRun(exec, repository, runId)));
|
|
222
230
|
}
|
|
223
231
|
async function checksForHead(input) {
|
|
224
|
-
const checks = await fetchPullRequestChecks(input.exec, input.repository, input.pr, { tolerateMissingChecks: Boolean(input.headSha) });
|
|
232
|
+
const checks = await fetchPullRequestChecks(input.exec, input.repository, input.pr, { requiredOnly: true, tolerateMissingChecks: Boolean(input.headSha) });
|
|
225
233
|
const targetChecks = [];
|
|
226
234
|
let hasAnyActionCheck = false;
|
|
227
235
|
let hasTargetActionCheck = false;
|
|
@@ -246,7 +254,6 @@ async function checksForHead(input) {
|
|
|
246
254
|
return {
|
|
247
255
|
blocking: targetChecks.filter((check) => isFailedCheck(check) || isCancelledCheck(check)),
|
|
248
256
|
hasAnyActionCheck,
|
|
249
|
-
hasAnyCheck: checks.length > 0,
|
|
250
257
|
hasPending: targetChecks.some(isPendingCheck),
|
|
251
258
|
hasTargetActionCheck,
|
|
252
259
|
};
|
|
@@ -343,6 +350,7 @@ async function classifyChecks(input) {
|
|
|
343
350
|
}
|
|
344
351
|
},
|
|
345
352
|
options: reviewer.options,
|
|
353
|
+
parentSessionId: input.parentSessionId,
|
|
346
354
|
parse: (text) => {
|
|
347
355
|
const output = parseCiClassificationOutput(text);
|
|
348
356
|
for (const check of output.checks) {
|
|
@@ -366,18 +374,20 @@ async function classifyChecks(input) {
|
|
|
366
374
|
const rawPath = input.outputDir
|
|
367
375
|
? join(input.outputDir, `${reviewer.key}.ci-classification.raw.txt`)
|
|
368
376
|
: undefined;
|
|
369
|
-
const
|
|
377
|
+
const checks = result.value.checks.map((check) => ({
|
|
378
|
+
classification: check.classification,
|
|
379
|
+
name: check.name,
|
|
380
|
+
reason: check.reason,
|
|
381
|
+
}));
|
|
370
382
|
if (rawPath)
|
|
371
383
|
await writeFile(rawPath, result.raw);
|
|
372
|
-
run.
|
|
384
|
+
run.checks = checks;
|
|
373
385
|
run.rawPath = rawPath;
|
|
374
|
-
run.reason = check?.reason;
|
|
375
386
|
run.sessionId = result.sessionId;
|
|
376
387
|
run.status = "completed";
|
|
377
388
|
await input.onClassifierProgress?.({
|
|
378
|
-
|
|
389
|
+
checks,
|
|
379
390
|
rawPath,
|
|
380
|
-
reason: check?.reason ?? "No classification reason was provided.",
|
|
381
391
|
reviewer: reviewer.key,
|
|
382
392
|
sessionId: result.sessionId,
|
|
383
393
|
type: "classifier_completed",
|
|
@@ -392,22 +402,20 @@ async function classifyChecks(input) {
|
|
|
392
402
|
reviewer: reviewer.key,
|
|
393
403
|
type: "classifier_failed",
|
|
394
404
|
});
|
|
395
|
-
|
|
405
|
+
throw error;
|
|
396
406
|
}
|
|
397
407
|
}, { signal: input.signal });
|
|
398
408
|
const threshold = majorityThreshold(reviewers.length);
|
|
399
409
|
return {
|
|
400
410
|
classified: input.checks.map((item) => {
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
const check = vote.output?.checks.find((output) => output.name === item.check.name);
|
|
411
|
+
const checkVotes = votes.map((vote) => {
|
|
412
|
+
const check = vote.output.checks.find((output) => output.name === item.check.name);
|
|
404
413
|
return {
|
|
405
414
|
classification: check?.classification ?? "SCOPE_IN",
|
|
406
415
|
reason: check?.reason ?? "Missing classification; treated as scope-in.",
|
|
407
416
|
reviewer: vote.reviewer,
|
|
408
417
|
};
|
|
409
418
|
});
|
|
410
|
-
const failures = votes.filter((vote) => !vote.output);
|
|
411
419
|
const scopeIn = checkVotes.filter((vote) => vote.classification === "SCOPE_IN");
|
|
412
420
|
const scopeOut = checkVotes.filter((vote) => vote.classification === "SCOPE_OUT");
|
|
413
421
|
const classification = scopeOut.length >= threshold
|
|
@@ -421,9 +429,6 @@ async function classifyChecks(input) {
|
|
|
421
429
|
const reasons = checkVotes
|
|
422
430
|
.filter((vote) => vote.classification === classification)
|
|
423
431
|
.map((vote) => `${vote.reviewer}: ${vote.reason}`);
|
|
424
|
-
for (const failure of failures) {
|
|
425
|
-
reasons.push(`${failure.reviewer}: classifier failed; vote ignored`);
|
|
426
|
-
}
|
|
427
432
|
return {
|
|
428
433
|
check: item.check,
|
|
429
434
|
classification,
|
|
@@ -457,15 +462,17 @@ export async function waitForChecksWithClassification(input) {
|
|
|
457
462
|
await input.onProgress?.("waiting for CI checks");
|
|
458
463
|
for (let attempt = 0;; attempt += 1) {
|
|
459
464
|
try {
|
|
460
|
-
await watchChecks(input.exec, input.repository, input.pr
|
|
465
|
+
await watchChecks(input.exec, input.repository, input.pr, {
|
|
466
|
+
requiredOnly: true,
|
|
467
|
+
});
|
|
461
468
|
}
|
|
462
469
|
catch {
|
|
463
470
|
// gh exits non-zero for pending checks too; re-read check state below.
|
|
464
471
|
}
|
|
465
472
|
const target = await readTargetChecks();
|
|
466
473
|
const waitingForTargetHead = Boolean(input.headSha) &&
|
|
467
|
-
|
|
468
|
-
|
|
474
|
+
target.hasAnyActionCheck &&
|
|
475
|
+
!target.hasTargetActionCheck;
|
|
469
476
|
if (!waitingForTargetHead && !target.hasPending) {
|
|
470
477
|
await assignBlockingChecks(target.blocking);
|
|
471
478
|
break;
|
|
@@ -505,6 +512,7 @@ export async function waitForChecksWithClassification(input) {
|
|
|
505
512
|
directory: input.directory,
|
|
506
513
|
onClassifierProgress: input.onClassifierProgress,
|
|
507
514
|
outputDir: input.outputDir,
|
|
515
|
+
parentSessionId: input.parentSessionId,
|
|
508
516
|
pr: input.pr,
|
|
509
517
|
repairAttempts: input.repairAttempts,
|
|
510
518
|
repository: input.repository,
|
|
@@ -545,8 +553,11 @@ export async function waitForChecksWithClassification(input) {
|
|
|
545
553
|
try {
|
|
546
554
|
await input.onProgress?.("waiting for rerun CI checks");
|
|
547
555
|
await watchRerunRuns(input.exec, input.repository, rerunnable);
|
|
548
|
-
if (input.wait)
|
|
549
|
-
await watchChecks(input.exec, input.repository, input.pr
|
|
556
|
+
if (input.wait) {
|
|
557
|
+
await watchChecks(input.exec, input.repository, input.pr, {
|
|
558
|
+
requiredOnly: true,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
550
561
|
}
|
|
551
562
|
catch {
|
|
552
563
|
// Re-read the PR checks below so stale failed checks are not trusted.
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { majorityThreshold } from "./majority";
|
|
2
|
+
function validationFindings(output) {
|
|
3
|
+
if ("findings" in output)
|
|
4
|
+
return output.findings;
|
|
5
|
+
return output.newFindings.map((finding) => ({
|
|
6
|
+
fix: "Please address this before merging.",
|
|
7
|
+
issue: finding.body,
|
|
8
|
+
line: finding.line,
|
|
9
|
+
path: finding.path,
|
|
10
|
+
startLine: finding.startLine,
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
2
13
|
export function reviewFindingTargets(outputs) {
|
|
3
14
|
return Object.entries(outputs).flatMap(([reviewer, output]) => {
|
|
4
15
|
if (output.verdict !== "CHANGES_REQUESTED")
|
|
5
16
|
return [];
|
|
6
|
-
return output.
|
|
17
|
+
return validationFindings(output).map((finding, findingIndex) => ({
|
|
7
18
|
finding,
|
|
8
19
|
findingIndex,
|
|
9
20
|
reviewer,
|
|
@@ -41,7 +52,9 @@ export function applyFindingValidation(input) {
|
|
|
41
52
|
next[reviewer] = output;
|
|
42
53
|
continue;
|
|
43
54
|
}
|
|
44
|
-
const
|
|
55
|
+
const keptIndexes = new Set();
|
|
56
|
+
const findings = validationFindings(output);
|
|
57
|
+
findings.forEach((finding, findingIndex) => {
|
|
45
58
|
let agrees = 1;
|
|
46
59
|
for (const validator of input.reviewerKeys) {
|
|
47
60
|
if (validator === reviewer)
|
|
@@ -53,14 +66,23 @@ export function applyFindingValidation(input) {
|
|
|
53
66
|
const target = { finding, findingIndex, reviewer };
|
|
54
67
|
if (agrees >= threshold) {
|
|
55
68
|
kept.push(target);
|
|
56
|
-
|
|
69
|
+
keptIndexes.add(findingIndex);
|
|
70
|
+
return;
|
|
57
71
|
}
|
|
58
72
|
discarded.push(target);
|
|
59
|
-
return false;
|
|
60
73
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
if ("findings" in output) {
|
|
75
|
+
const keptFindings = output.findings.filter((_finding, index) => keptIndexes.has(index));
|
|
76
|
+
next[reviewer] = keptFindings.length
|
|
77
|
+
? { ...output, findings: keptFindings }
|
|
78
|
+
: { findings: [], verdict: "MERGE" };
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const newFindings = output.newFindings.filter((_finding, index) => keptIndexes.has(index));
|
|
82
|
+
next[reviewer] =
|
|
83
|
+
newFindings.length || output.followUps.length
|
|
84
|
+
? { ...output, newFindings }
|
|
85
|
+
: { ...output, newFindings, verdict: "MERGE" };
|
|
64
86
|
}
|
|
65
87
|
return { outputs: next, summary: { discarded, kept } };
|
|
66
88
|
}
|
|
@@ -10,7 +10,7 @@ export function aggregateStringMajority(results, votes) {
|
|
|
10
10
|
const voters = Object.fromEntries(votes.map((vote) => [vote, []]));
|
|
11
11
|
for (const result of results) {
|
|
12
12
|
counts[result.vote] += 1;
|
|
13
|
-
voters[result.vote].push(result.
|
|
13
|
+
voters[result.vote].push(result.voter);
|
|
14
14
|
}
|
|
15
15
|
const threshold = majorityThreshold(results.length);
|
|
16
16
|
const vote = votes.find((item) => counts[item] >= threshold);
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { prRunOutputDir } from "../config/output";
|
|
4
|
-
import { closePullRequest, configureGitIdentity, fetchMergeQueueRequirement, fetchPullRequest, fetchUnresolvedThreads, mergePullRequest, postApproval, postChangesRequested, postCloseComment, postReply, pushHead, removeWorktree, resolveThread,
|
|
4
|
+
import { closePullRequest, configureGitIdentity, fetchMergeQueueRequirement, fetchPullRequest, fetchUnresolvedThreads, mergePullRequest, postApproval, postChangesRequested, postCloseComment, postReply, pushHead, removeWorktree, resolveThread, waitForAutoMerge, waitForMergeQueue, } from "../github/commands";
|
|
5
5
|
import { composeEditPrompt, composeRereviewCloseReconsiderationPrompt, composeRereviewPrompt, } from "../prompts/compose";
|
|
6
6
|
import { parseEditOutput, parseRereviewCloseReconsiderationOutput, parseRereviewOutput, } from "../prompts/output";
|
|
7
7
|
import { throwIfAborted, withAbortSignal } from "./abort";
|
|
8
8
|
import { waitForChecksWithClassification } from "./ci";
|
|
9
|
-
import {
|
|
9
|
+
import { validateInlineCommentTargets, } from "./inline-comments";
|
|
10
10
|
import { closeMinorityReviewers, mergeVerdictForPolicy } from "./majority";
|
|
11
11
|
import { runModelWithRepair } from "./model";
|
|
12
12
|
import { mapPool } from "./pool";
|
|
13
13
|
import { formatMergeReport } from "./report";
|
|
14
|
-
import { runReview } from "./review";
|
|
14
|
+
import { inlineCommentTargetsForDiff, runReview, } from "./review";
|
|
15
15
|
import { checkSafetyGate, hasSafetyGate } from "./safety";
|
|
16
16
|
function outputDir(input) {
|
|
17
17
|
return prRunOutputDir({
|
|
@@ -53,7 +53,7 @@ async function withReviewerFailureProgress(input) {
|
|
|
53
53
|
async function runEditor(input, worktreePath, cycle, reviewFindings, unresolvedThreads) {
|
|
54
54
|
const editor = input.repository.agents.editor;
|
|
55
55
|
if (!editor)
|
|
56
|
-
throw new Error("
|
|
56
|
+
throw new Error("merge.editor is required for magi_merge");
|
|
57
57
|
throwIfAborted(input.signal);
|
|
58
58
|
await configureGitIdentity(input.exec, worktreePath, {
|
|
59
59
|
email: editor.author?.email,
|
|
@@ -96,6 +96,7 @@ async function runEditor(input, worktreePath, cycle, reviewFindings, unresolvedT
|
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
options: editor.options,
|
|
99
|
+
parentSessionId: input.parentSessionId,
|
|
99
100
|
parse: parseEditOutput,
|
|
100
101
|
permission: editor.permission,
|
|
101
102
|
prompt,
|
|
@@ -195,7 +196,21 @@ async function runRereview(input, worktreePath, previousHeadSha, cycle, sessionI
|
|
|
195
196
|
throwIfAborted(input.signal);
|
|
196
197
|
const meta = await fetchPullRequest(input.exec, input.repository, input.pr);
|
|
197
198
|
const headSha = options.dryRunHeadSha ?? meta.headRefOid;
|
|
198
|
-
const inlineCommentTargets =
|
|
199
|
+
const inlineCommentTargets = await inlineCommentTargetsForDiff({
|
|
200
|
+
ensure: options.dryRunHeadSha
|
|
201
|
+
? undefined
|
|
202
|
+
: {
|
|
203
|
+
fromSource: "base",
|
|
204
|
+
meta,
|
|
205
|
+
repository: input.repository,
|
|
206
|
+
toSource: "head",
|
|
207
|
+
},
|
|
208
|
+
exec: input.exec,
|
|
209
|
+
fromSha: meta.baseRefOid,
|
|
210
|
+
range: "direct",
|
|
211
|
+
toSha: headSha,
|
|
212
|
+
worktreePath,
|
|
213
|
+
});
|
|
199
214
|
const artifactDir = outputDir(input);
|
|
200
215
|
let entries = await mapPool(input.repository.agents.reviewers, input.repository.concurrency.reviewers, async (reviewer) => {
|
|
201
216
|
throwIfAborted(input.signal);
|
|
@@ -250,6 +265,7 @@ async function runRereview(input, worktreePath, previousHeadSha, cycle, sessionI
|
|
|
250
265
|
}
|
|
251
266
|
},
|
|
252
267
|
options: reviewer.options,
|
|
268
|
+
parentSessionId: input.parentSessionId,
|
|
253
269
|
parse: (text) => parseRereviewOutputWithInlineTargets(text, inlineCommentTargets),
|
|
254
270
|
permission: reviewer.permission,
|
|
255
271
|
prompt,
|
|
@@ -294,7 +310,7 @@ async function runRereview(input, worktreePath, previousHeadSha, cycle, sessionI
|
|
|
294
310
|
baseSha: meta.baseRefOid,
|
|
295
311
|
closeReason: entry.output.reason,
|
|
296
312
|
directory: input.directory,
|
|
297
|
-
headSha
|
|
313
|
+
headSha,
|
|
298
314
|
includeReviewGuidelines: !hasReviewerSession,
|
|
299
315
|
includeSessionContext: !hasReviewerSession,
|
|
300
316
|
pr: input.pr,
|
|
@@ -333,6 +349,7 @@ async function runRereview(input, worktreePath, previousHeadSha, cycle, sessionI
|
|
|
333
349
|
}
|
|
334
350
|
},
|
|
335
351
|
options: reviewer.options,
|
|
352
|
+
parentSessionId: input.parentSessionId,
|
|
336
353
|
parse: (text) => {
|
|
337
354
|
const output = parseRereviewCloseReconsiderationOutput(text);
|
|
338
355
|
validateInlineCommentTargets(output.newFindings, inlineCommentTargets, "newFindings");
|
|
@@ -409,8 +426,11 @@ async function finishMergeRun(input, result, reportInput) {
|
|
|
409
426
|
}
|
|
410
427
|
async function mergeWithQueue(input, exec, editorAccount) {
|
|
411
428
|
await mergePullRequest(exec, input.repository, input.pr, editorAccount);
|
|
412
|
-
if (!input.repository.merge.mergeQueue)
|
|
413
|
-
|
|
429
|
+
if (!input.repository.merge.mergeQueue) {
|
|
430
|
+
if (!input.repository.merge.auto)
|
|
431
|
+
return "merged";
|
|
432
|
+
return waitForAutoMerge(exec, input.repository, input.pr);
|
|
433
|
+
}
|
|
414
434
|
return waitForMergeQueue(exec, input.repository, input.pr);
|
|
415
435
|
}
|
|
416
436
|
export function hasBlockingCiReports(reports) {
|
|
@@ -570,7 +590,7 @@ export async function runMerge(input) {
|
|
|
570
590
|
const abortableInput = { ...input, exec };
|
|
571
591
|
const editor = input.repository.agents.editor;
|
|
572
592
|
if (!editor)
|
|
573
|
-
throw new Error("
|
|
593
|
+
throw new Error("merge.editor is required for magi_merge");
|
|
574
594
|
throwIfAborted(input.signal);
|
|
575
595
|
const artifactDir = outputDir(input);
|
|
576
596
|
await mkdir(artifactDir, { recursive: true });
|
|
@@ -701,7 +721,7 @@ export async function runMerge(input) {
|
|
|
701
721
|
threads: unresolvedThreads,
|
|
702
722
|
});
|
|
703
723
|
const editorFindings = blockingReviewFindings(reportOutputs);
|
|
704
|
-
const editableFindings =
|
|
724
|
+
const editableFindings = editorFindings;
|
|
705
725
|
const findingAttemptsExhausted = input.repository.merge.maxThreadResolutionCycles !== 0 &&
|
|
706
726
|
cycle > input.repository.merge.maxThreadResolutionCycles;
|
|
707
727
|
if (!editableThreads.length &&
|
|
@@ -781,6 +801,7 @@ export async function runMerge(input) {
|
|
|
781
801
|
exec,
|
|
782
802
|
headSha: editedHeadSha,
|
|
783
803
|
onProgress: (phase) => input.onProgress?.({ phase, type: "phase" }),
|
|
804
|
+
parentSessionId: input.parentSessionId,
|
|
784
805
|
pr: input.pr,
|
|
785
806
|
repairAttempts: input.config.output?.repairAttempts ?? 3,
|
|
786
807
|
repository: input.repository,
|
|
@@ -89,6 +89,7 @@ function extractText(result, allowEmpty = false) {
|
|
|
89
89
|
export async function createModelSession(input) {
|
|
90
90
|
return extractSessionId(await input.client.session.create({
|
|
91
91
|
body: {
|
|
92
|
+
parentID: input.parentSessionId,
|
|
92
93
|
permission: toOpenCodePermissionRules(input.permission),
|
|
93
94
|
title: input.title,
|
|
94
95
|
},
|
|
@@ -96,15 +97,26 @@ export async function createModelSession(input) {
|
|
|
96
97
|
}
|
|
97
98
|
export async function promptModelText(input) {
|
|
98
99
|
throwIfAborted(input.signal);
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
const abort = () => {
|
|
101
|
+
void input.client.session
|
|
102
|
+
.abort?.({ path: { id: input.sessionId } })
|
|
103
|
+
.catch(() => undefined);
|
|
104
|
+
};
|
|
105
|
+
input.signal?.addEventListener("abort", abort, { once: true });
|
|
106
|
+
try {
|
|
107
|
+
const result = await input.client.session.prompt({
|
|
108
|
+
body: {
|
|
109
|
+
model: modelBody(input.model),
|
|
110
|
+
parts: [{ type: "text", text: input.prompt }],
|
|
111
|
+
},
|
|
112
|
+
path: { id: input.sessionId },
|
|
113
|
+
});
|
|
114
|
+
throwIfAborted(input.signal);
|
|
115
|
+
return extractText(result, input.allowEmpty);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
input.signal?.removeEventListener("abort", abort);
|
|
119
|
+
}
|
|
108
120
|
}
|
|
109
121
|
async function sendPrompt(client, sessionId, model, prompt, signal) {
|
|
110
122
|
return promptModelText({ client, model, prompt, sessionId, signal });
|
|
@@ -113,6 +125,7 @@ export async function runModelText(input) {
|
|
|
113
125
|
throwIfAborted(input.signal);
|
|
114
126
|
const sessionId = await createModelSession({
|
|
115
127
|
client: input.client,
|
|
128
|
+
parentSessionId: input.parentSessionId,
|
|
116
129
|
permission: input.permission,
|
|
117
130
|
title: input.title,
|
|
118
131
|
});
|
|
@@ -152,6 +165,7 @@ export async function runModelWithRepair(input) {
|
|
|
152
165
|
? input.sessionId
|
|
153
166
|
: extractSessionId(await input.client.session.create({
|
|
154
167
|
body: {
|
|
168
|
+
parentID: input.parentSessionId,
|
|
155
169
|
permission: toOpenCodePermissionRules(input.permission),
|
|
156
170
|
title: input.title,
|
|
157
171
|
},
|