| author | |
| committer | |
| log | 57f96e69d39669f47e7310062222cd67adbb4912 |
| tree | 49c2dd93c6ded17485b797e04baf8e89e7cc251c |
| parent | ff9eb8bd0c8e71f98969a58d5789889e07b57c8b |
| signature | Signed by PGP key B5690EEEBB952194 |
7 files changed, 171 insertions(+), 25 deletions(-)
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts+38-2| ... | ... | @@ -7029,13 +7029,17 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7029 | 7029 | : { |
| 7030 | 7030 | thread: { |
| 7031 | 7031 | id: name.includes("wrong child") ? "other-child" : threadId, |
| 7032 | ...(name.startsWith("current Codex") ? { model: "gpt-6-sol" } : {}), | |
| 7032 | ...(name.startsWith("current Codex") | |
| 7033 | ? { model: "gpt-6-sol", reasoningEffort: "high" } | |
| 7034 | : {}), | |
| 7033 | 7035 | }, |
| 7034 | 7036 | model: name.startsWith("current Codex") |
| 7035 | 7037 | ? null |
| 7036 | 7038 | : name === "wrong child" |
| 7037 | 7039 | ? "gpt-5.6-sol" |
| 7038 | 7040 | : model, |
| 7041 | reasoningEffort: "high", | |
| 7042 | serviceTier: "priority", | |
| 7039 | 7043 | }, |
| 7040 | 7044 | ), |
| 7041 | 7045 | ); |
| ... | ... | @@ -7055,6 +7059,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7055 | 7059 | yield* TestClock.adjust("100 millis"); |
| 7056 | 7060 | yield* harness.firstTerminal; |
| 7057 | 7061 | assert.equal(harness.subagentUpdates().at(-1)?.subagent.model, model); |
| 7062 | if (model) { | |
| 7063 | assert.deepEqual(harness.subagentUpdates().at(-1)?.subagent.modelSelection?.options, [ | |
| 7064 | { id: "reasoningEffort", value: "high" }, | |
| 7065 | ...(name.startsWith("current Codex") ? [] : [{ id: "serviceTier", value: "priority" }]), | |
| 7066 | ]); | |
| 7067 | } | |
| 7058 | 7068 | assert.equal(metadataRequests, name === "current Codex Sol" ? 1 : 2); |
| 7059 | 7069 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), |
| 7060 | 7070 | ), |
| ... | ... | @@ -7142,6 +7152,8 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7142 | 7152 | threadId: RESUME_CHILD_THREAD, |
| 7143 | 7153 | threadSettings: { |
| 7144 | 7154 | model, |
| 7155 | effort: "low", | |
| 7156 | serviceTier: "ultrafast", | |
| 7145 | 7157 | modelProvider: "openai", |
| 7146 | 7158 | cwd: "/workspace", |
| 7147 | 7159 | approvalPolicy: "never", |
| ... | ... | @@ -7152,12 +7164,32 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7152 | 7164 | }, |
| 7153 | 7165 | }, |
| 7154 | 7166 | }; |
| 7167 | const initialSettings: CodexReplay.CodexAppServerReplayEntry = { | |
| 7168 | type: "emit_inbound", | |
| 7169 | frame: { | |
| 7170 | method: "thread/settings/updated", | |
| 7171 | params: { | |
| 7172 | threadId: RESUME_CHILD_THREAD, | |
| 7173 | threadSettings: { | |
| 7174 | model: "gpt-6-astra", | |
| 7175 | effort: "low", | |
| 7176 | serviceTier: "ultrafast", | |
| 7177 | modelProvider: "openai", | |
| 7178 | cwd: "/workspace", | |
| 7179 | approvalPolicy: "never", | |
| 7180 | approvalsReviewer: "auto_review", | |
| 7181 | collaborationMode: { mode: "default", settings: { model: "gpt-6-astra" } }, | |
| 7182 | sandboxPolicy: { type: "dangerFullAccess" }, | |
| 7183 | }, | |
| 7184 | }, | |
| 7185 | }, | |
| 7186 | }; | |
| 7155 | 7187 | const harness = yield* makeCodexReplayHarness( |
| 7156 | 7188 | { |
| 7157 | 7189 | ...resumeSubagentTranscript, |
| 7158 | 7190 | entries: resumeSubagentTranscript.entries.flatMap((entry) => |
| 7159 | 7191 | entry.type === "emit_inbound" && entry.label === "turn/completed/root" |
| 7160 | ? [entry, notification] | |
| 7192 | ? [entry, initialSettings, notification] | |
| 7161 | 7193 | : [entry], |
| 7162 | 7194 | ), |
| 7163 | 7195 | }, |
| ... | ... | @@ -7186,6 +7218,10 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7186 | 7218 | yield* Deferred.succeed(releaseMetadata, undefined); |
| 7187 | 7219 | yield* TestClock.adjust("30 seconds"); |
| 7188 | 7220 | assert.equal(harness.subagentUpdates().at(-1)?.subagent.model, model); |
| 7221 | assert.deepEqual(harness.subagentUpdates().at(-1)?.subagent.modelSelection?.options, [ | |
| 7222 | { id: "reasoningEffort", value: "low" }, | |
| 7223 | { id: "serviceTier", value: "ultrafast" }, | |
| 7224 | ]); | |
| 7189 | 7225 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), |
| 7190 | 7226 | ), |
| 7191 | 7227 | ); |
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts+85-18| ... | ... | @@ -1352,18 +1352,20 @@ const decodeCodexResumeMetadata = Schema.decodeUnknownEffect( |
| 1352 | 1352 | Schema.Struct({ thread: Schema.Struct({ id: Schema.String, updatedAt: Schema.Number }) }), |
| 1353 | 1353 | ); |
| 1354 | 1354 | |
| 1355 | const decodeCodexChildModel = Schema.decodeUnknownEffect( | |
| 1356 | Schema.Struct({ | |
| 1357 | thread: Schema.Struct({ id: Schema.String }), | |
| 1358 | model: Schema.NullOr(Schema.String), | |
| 1359 | }), | |
| 1360 | ); | |
| 1355 | const CodexChildSelection = Schema.Struct({ | |
| 1356 | thread: Schema.Struct({ id: Schema.String }), | |
| 1357 | model: Schema.NullOr(Schema.String), | |
| 1358 | reasoningEffort: Schema.optional(Schema.NullOr(Schema.String)), | |
| 1359 | serviceTier: Schema.optional(Schema.NullOr(Schema.String)), | |
| 1360 | }); | |
| 1361 | const decodeCodexChildModel = Schema.decodeUnknownEffect(CodexChildSelection); | |
| 1361 | 1362 | |
| 1362 | 1363 | const decodeCodexChildThread = Schema.decodeUnknownEffect( |
| 1363 | 1364 | Schema.Struct({ |
| 1364 | 1365 | thread: Schema.Struct({ |
| 1365 | 1366 | id: Schema.String, |
| 1366 | 1367 | model: Schema.optional(Schema.NullOr(Schema.String)), |
| 1368 | reasoningEffort: Schema.optional(Schema.NullOr(Schema.String)), | |
| 1367 | 1369 | }), |
| 1368 | 1370 | }), |
| 1369 | 1371 | ); |
| ... | ... | @@ -1834,7 +1836,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 1834 | 1836 | const pendingRootTurns = yield* Ref.make(new Map<string, ProviderAdapterV2TurnInput>()); |
| 1835 | 1837 | const turnWaiters = yield* Ref.make(new Map<string, Deferred.Deferred<void, never>>()); |
| 1836 | 1838 | const subagentThreads = yield* Ref.make(new Map<string, CodexSubagentThreadContext>()); |
| 1837 | const subagentModels = new Map<string, string>(); | |
| 1839 | const subagentSelections = new Map<string, Omit<ModelSelection, "instanceId">>(); | |
| 1838 | 1840 | const pendingSubagentTurns = yield* Ref.make( |
| 1839 | 1841 | new Map<string, ReadonlyArray<PendingCodexSubagentTurnStarted>>(), |
| 1840 | 1842 | ); |
| ... | ... | @@ -2669,16 +2671,51 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 2669 | 2671 | }); |
| 2670 | 2672 | }); |
| 2671 | 2673 | |
| 2672 | const updateSubagentModel = Effect.fnUntraced(function* ( | |
| 2674 | const updateSubagentSelection = Effect.fnUntraced(function* ( | |
| 2673 | 2675 | nativeThreadId: string, |
| 2674 | 2676 | value: string | null, |
| 2677 | effort?: string | null, | |
| 2678 | tier?: string | null, | |
| 2675 | 2679 | ) { |
| 2676 | 2680 | const model = value?.trim(); |
| 2677 | 2681 | if (!model) return; |
| 2678 | subagentModels.set(nativeThreadId, model); | |
| 2682 | const previous = subagentSelections.get(nativeThreadId); | |
| 2683 | const previousOptions = previous?.options; | |
| 2684 | const options = | |
| 2685 | effort === undefined && tier === undefined | |
| 2686 | ? previousOptions | |
| 2687 | : [ | |
| 2688 | ...(previousOptions ?? []).filter(({ id }) => | |
| 2689 | id === "reasoningEffort" | |
| 2690 | ? effort === undefined | |
| 2691 | : id === "serviceTier" && tier === undefined, | |
| 2692 | ), | |
| 2693 | ...[ | |
| 2694 | { id: "reasoningEffort", value: effort }, | |
| 2695 | { id: "serviceTier", value: tier }, | |
| 2696 | ].flatMap(({ id, value }) => | |
| 2697 | value?.trim() ? [{ id, value: value.trim() }] : [], | |
| 2698 | ), | |
| 2699 | ]; | |
| 2700 | const selection = { | |
| 2701 | model, | |
| 2702 | ...(options === undefined ? {} : { options }), | |
| 2703 | }; | |
| 2704 | subagentSelections.set(nativeThreadId, selection); | |
| 2679 | 2705 | const subagent = (yield* Ref.get(subagentThreads)).get(nativeThreadId); |
| 2680 | if (subagent === undefined || subagent.task.model === model) return; | |
| 2681 | subagent.task = { ...subagent.task, model, updatedAt: yield* DateTime.now }; | |
| 2706 | if (subagent === undefined) return; | |
| 2707 | const modelSelection = { instanceId: subagent.task.providerInstanceId, ...selection }; | |
| 2708 | if ( | |
| 2709 | subagent.task.modelSelection && | |
| 2710 | modelSelectionsEqual(subagent.task.modelSelection, modelSelection) | |
| 2711 | ) | |
| 2712 | return; | |
| 2713 | subagent.task = { | |
| 2714 | ...subagent.task, | |
| 2715 | model, | |
| 2716 | modelSelection, | |
| 2717 | updatedAt: yield* DateTime.now, | |
| 2718 | }; | |
| 2682 | 2719 | yield* emitProviderEvent({ |
| 2683 | 2720 | type: "subagent.updated", |
| 2684 | 2721 | driver: CODEX_PROVIDER, |
| ... | ... | @@ -2694,6 +2731,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 2694 | 2731 | readonly prompt: string; |
| 2695 | 2732 | readonly title: string | null; |
| 2696 | 2733 | readonly model: string | null; |
| 2734 | readonly reasoningEffort?: string | null | undefined; | |
| 2697 | 2735 | readonly ordinal: number; |
| 2698 | 2736 | readonly emitInitialPrompt: boolean; |
| 2699 | 2737 | }) => |
| ... | ... | @@ -2702,6 +2740,13 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 2702 | 2740 | if (registeredSubagents.has(input.nativeThreadId)) { |
| 2703 | 2741 | return; |
| 2704 | 2742 | } |
| 2743 | if (input.model && !subagentSelections.has(input.nativeThreadId)) { | |
| 2744 | yield* updateSubagentSelection( | |
| 2745 | input.nativeThreadId, | |
| 2746 | input.model, | |
| 2747 | input.reasoningEffort, | |
| 2748 | ); | |
| 2749 | } | |
| 2705 | 2750 | |
| 2706 | 2751 | const now = yield* DateTime.now; |
| 2707 | 2752 | const subagentNodeId = idAllocator.derive.nodeFromProviderItem({ |
| ... | ... | @@ -2744,6 +2789,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 2744 | 2789 | createdAt: now, |
| 2745 | 2790 | updatedAt: now, |
| 2746 | 2791 | } satisfies OrchestrationV2ProviderThread; |
| 2792 | const reportedSelection = subagentSelections.get(input.nativeThreadId); | |
| 2747 | 2793 | const task = { |
| 2748 | 2794 | id: subagentNodeId, |
| 2749 | 2795 | threadId: input.context.projectionThreadId, |
| ... | ... | @@ -2758,7 +2804,13 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 2758 | 2804 | nativeTaskRef: codexNativeItemRef(input.nativeItemId), |
| 2759 | 2805 | prompt: input.prompt, |
| 2760 | 2806 | title: input.title, |
| 2761 | model: subagentModels.get(input.nativeThreadId) ?? input.model, | |
| 2807 | model: reportedSelection?.model ?? input.model, | |
| 2808 | modelSelection: reportedSelection | |
| 2809 | ? { | |
| 2810 | instanceId: input.context.input.modelSelection.instanceId, | |
| 2811 | ...reportedSelection, | |
| 2812 | } | |
| 2813 | : undefined, | |
| 2762 | 2814 | status: "running", |
| 2763 | 2815 | result: null, |
| 2764 | 2816 | startedAt: now, |
| ... | ... | @@ -2913,9 +2965,13 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 2913 | 2965 | .request("thread/read", { threadId: input.nativeThreadId, includeTurns: false }) |
| 2914 | 2966 | .pipe( |
| 2915 | 2967 | Effect.flatMap(decodeCodexChildThread), |
| 2916 | Effect.map((response) => | |
| 2968 | Effect.map((response): typeof CodexChildSelection.Type | null => | |
| 2917 | 2969 | response.thread.id === input.nativeThreadId && response.thread.model?.trim() |
| 2918 | ? { thread: response.thread, model: response.thread.model } | |
| 2970 | ? { | |
| 2971 | thread: response.thread, | |
| 2972 | model: response.thread.model, | |
| 2973 | reasoningEffort: response.thread.reasoningEffort, | |
| 2974 | } | |
| 2919 | 2975 | : null, |
| 2920 | 2976 | ), |
| 2921 | 2977 | Effect.catch(() => Effect.succeed(null)), |
| ... | ... | @@ -2932,8 +2988,13 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 2932 | 2988 | Effect.timeout("5 seconds"), |
| 2933 | 2989 | Effect.flatMap((response) => |
| 2934 | 2990 | response.thread.id === input.nativeThreadId && |
| 2935 | !subagentModels.has(input.nativeThreadId) | |
| 2936 | ? updateSubagentModel(input.nativeThreadId, response.model) | |
| 2991 | !subagentSelections.has(input.nativeThreadId) | |
| 2992 | ? updateSubagentSelection( | |
| 2993 | input.nativeThreadId, | |
| 2994 | response.model, | |
| 2995 | response.reasoningEffort, | |
| 2996 | response.serviceTier, | |
| 2997 | ) | |
| 2937 | 2998 | : Effect.void, |
| 2938 | 2999 | ), |
| 2939 | 3000 | Effect.catch(() => Effect.void), |
| ... | ... | @@ -2964,6 +3025,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 2964 | 3025 | prompt: input.item.prompt ?? "", |
| 2965 | 3026 | title: null, |
| 2966 | 3027 | model, |
| 3028 | reasoningEffort: input.item.reasoningEffort, | |
| 2967 | 3029 | ordinal: index + 1, |
| 2968 | 3030 | emitInitialPrompt: true, |
| 2969 | 3031 | }); |
| ... | ... | @@ -4296,10 +4358,15 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 4296 | 4358 | ); |
| 4297 | 4359 | |
| 4298 | 4360 | yield* client.handleServerNotification("thread/settings/updated", (payload) => |
| 4299 | updateSubagentModel(payload.threadId, payload.threadSettings.model), | |
| 4361 | updateSubagentSelection( | |
| 4362 | payload.threadId, | |
| 4363 | payload.threadSettings.model, | |
| 4364 | payload.threadSettings.effort, | |
| 4365 | payload.threadSettings.serviceTier, | |
| 4366 | ), | |
| 4300 | 4367 | ); |
| 4301 | 4368 | yield* client.handleServerNotification("model/rerouted", (payload) => |
| 4302 | updateSubagentModel(payload.threadId, payload.toModel), | |
| 4369 | updateSubagentSelection(payload.threadId, payload.toModel), | |
| 4303 | 4370 | ); |
| 4304 | 4371 | |
| 4305 | 4372 | yield* client.handleServerNotification("turn/started", (payload) => |
apps/web/src/components/chat/SubagentTooltipContent.tsx+5-4| ... | ... | @@ -35,6 +35,7 @@ export function SubagentTooltipContent(props: { |
| 35 | 35 | model: string | null; |
| 36 | 36 | providerInstanceId: ProviderInstanceId; |
| 37 | 37 | origin: OrchestrationV2Subagent["origin"]; |
| 38 | modelSelection?: OrchestrationV2Subagent["modelSelection"]; | |
| 38 | 39 | provider?: ServerProvider | undefined; |
| 39 | 40 | /** The environment's instances; with several accounts on one provider, the card names this one. */ |
| 40 | 41 | providers?: ReadonlyArray<ServerProvider> | undefined; |
| ... | ... | @@ -57,7 +58,9 @@ export function SubagentTooltipContent(props: { |
| 57 | 58 | ? resolveSelectableModel(props.provider.driver, model, props.provider.models) |
| 58 | 59 | : model; |
| 59 | 60 | const providerModel = props.provider?.models.find((candidate) => candidate.slug === modelSlug); |
| 60 | const childSelection = props.childThread?.modelSelection; | |
| 61 | const childSelection = | |
| 62 | props.modelSelection ?? | |
| 63 | (props.origin === "app_owned" ? props.childThread?.modelSelection : undefined); | |
| 61 | 64 | const childModel = props.provider |
| 62 | 65 | ? (resolveSelectableModel( |
| 63 | 66 | props.provider.driver, |
| ... | ... | @@ -67,9 +70,7 @@ export function SubagentTooltipContent(props: { |
| 67 | 70 | : childSelection?.model.trim(); |
| 68 | 71 | const provider = props.provider; |
| 69 | 72 | const matchingSelection = |
| 70 | props.origin === "app_owned" && | |
| 71 | childModel === (modelSlug ?? model) && | |
| 72 | childSelection?.instanceId === props.providerInstanceId | |
| 73 | childModel === (modelSlug ?? model) && childSelection?.instanceId === props.providerInstanceId | |
| 73 | 74 | ? childSelection |
| 74 | 75 | : undefined; |
| 75 | 76 | const effort = ["reasoningEffort", "effort", "reasoning", "variant"] |
apps/web/src/components/chat/ThreadRelationshipsControl.agents.test.tsx+33| ... | ... | @@ -579,6 +579,39 @@ it("shows readable models and only differing workspace details in agent tooltips |
| 579 | 579 | expect(text(true)).toContain("My GPT · Work account · high"); |
| 580 | 580 | expect(text()).toContain("My GPT · Work account · Fast mode onhigh"); |
| 581 | 581 | expect(text()).not.toContain("Personal account"); |
| 582 | state.shells = []; | |
| 583 | for (const status of ["running", "completed", "failed"] as const) { | |
| 584 | state.projection = { | |
| 585 | ...projection, | |
| 586 | subagents: [ | |
| 587 | { | |
| 588 | ...projection.subagents[0], | |
| 589 | origin: "provider_native", | |
| 590 | status, | |
| 591 | modelSelection: { | |
| 592 | instanceId: "codex", | |
| 593 | model: "gpt-5.4", | |
| 594 | options: [ | |
| 595 | { id: "reasoningEffort", value: "low" }, | |
| 596 | { id: "serviceTier", value: "ultrafast" }, | |
| 597 | ], | |
| 598 | }, | |
| 599 | }, | |
| 600 | ], | |
| 601 | }; | |
| 602 | await act(async () => renderer.update(cloneElement(panel))); | |
| 603 | if (status === "completed") { | |
| 604 | await act(async () => | |
| 605 | renderer.root.findByProps({ type: "button", "aria-expanded": false }).props.onClick(), | |
| 606 | ); | |
| 607 | } | |
| 608 | expect(text()).toContain("My GPT · Work account · Ultrafast mode onlow"); | |
| 609 | expect(text()).not.toContain(" · high"); | |
| 610 | } | |
| 611 | await act(async () => | |
| 612 | renderer.root.findByProps({ type: "button", "aria-expanded": true }).props.onClick(), | |
| 613 | ); | |
| 614 | state.projection = projection; | |
| 582 | 615 | state.configs.set("test", speedConfig); |
| 583 | 616 | child.modelSelection.options = [{ id: "reasoningEffort", value: "high" }]; |
| 584 | 617 | state.shells = [ |
apps/web/src/components/chat/ThreadRelationshipsControl.tsx+2| ... | ... | @@ -212,6 +212,7 @@ export function ThreadRelationshipsPanel(props: { |
| 212 | 212 | driver: subagent.driver, |
| 213 | 213 | providerInstanceId: subagent.providerInstanceId, |
| 214 | 214 | origin: subagent.origin, |
| 215 | modelSelection: subagent.modelSelection, | |
| 215 | 216 | }, |
| 216 | 217 | ]), |
| 217 | 218 | ), |
| ... | ... | @@ -412,6 +413,7 @@ export function ThreadRelationshipsPanel(props: { |
| 412 | 413 | model={agent.model} |
| 413 | 414 | providerInstanceId={agent.providerInstanceId} |
| 414 | 415 | origin={agent.origin} |
| 416 | modelSelection={agent.modelSelection} | |
| 415 | 417 | provider={provider} |
| 416 | 418 | providers={providers} |
| 417 | 419 | driver={providerDriver} |
apps/web/src/components/chat/V2LifecycleRow.tsx+7-1| ... | ... | @@ -522,6 +522,7 @@ function SubagentTimelineLink(props: { |
| 522 | 522 | {...props} |
| 523 | 523 | elapsed={agent ? <AgentElapsed agent={projectedSubagentsToRuntime([agent])[0]!} /> : null} |
| 524 | 524 | model={agent?.model ?? null} |
| 525 | modelSelection={agent?.modelSelection} | |
| 525 | 526 | status={liveStatus} |
| 526 | 527 | result={agent?.result ?? props.result} |
| 527 | 528 | progress={agent?.progress ?? props.progress} |
| ... | ... | @@ -532,7 +533,11 @@ function SubagentTimelineLink(props: { |
| 532 | 533 | } |
| 533 | 534 | |
| 534 | 535 | function SubagentTimelineTooltip( |
| 535 | props: Parameters<typeof SubagentTimelineLink>[0] & { model: string | null; elapsed: ReactNode }, | |
| 536 | props: Parameters<typeof SubagentTimelineLink>[0] & { | |
| 537 | model: string | null; | |
| 538 | modelSelection: OrchestrationV2Subagent["modelSelection"]; | |
| 539 | elapsed: ReactNode; | |
| 540 | }, | |
| 536 | 541 | ) { |
| 537 | 542 | const environmentId = props.parentRef.environmentId; |
| 538 | 543 | const parent = useThreadShell(props.parentRef)?.source; |
| ... | ... | @@ -549,6 +554,7 @@ function SubagentTimelineTooltip( |
| 549 | 554 | model={props.model} |
| 550 | 555 | providerInstanceId={props.providerInstanceId} |
| 551 | 556 | origin={props.origin} |
| 557 | modelSelection={props.modelSelection} | |
| 552 | 558 | provider={props.provider} |
| 553 | 559 | providers={props.providers} |
| 554 | 560 | driver={props.driver} |
packages/contracts/src/orchestrationV2.ts+1| ... | ... | @@ -675,6 +675,7 @@ export const OrchestrationV2Subagent = Schema.Struct({ |
| 675 | 675 | prompt: Schema.String, |
| 676 | 676 | title: Schema.NullOr(Schema.String), |
| 677 | 677 | model: Schema.NullOr(Schema.String), |
| 678 | modelSelection: Schema.optional(ModelSelection), | |
| 678 | 679 | // Parent-wake policy for app-owned tasks: "always" offers a continuation on |
| 679 | 680 | // every terminal (async delegations; queue_after_active sequences it behind |
| 680 | 681 | // a live parent run), "settled_only" offers only when the parent has no |