@north-light/crouter 0.3.281 → 0.3.283
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/api/client.d.ts +19 -3
- package/dist/api/client.js +34 -0
- package/dist/api/dto/broker-ops.d.ts +11 -0
- package/dist/api/dto/broker.d.ts +3 -8
- package/dist/api/dto/common.d.ts +1 -1
- package/dist/api/dto/messages.d.ts +2 -2
- package/dist/api/dto/node-outcomes.d.ts +80 -0
- package/dist/api/dto/node-outcomes.js +2 -0
- package/dist/api/dto/nodes.d.ts +9 -0
- package/dist/api/dto/profiles.d.ts +5 -0
- package/dist/api/dto/reports.d.ts +45 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/plugin-manifest-schema.d.ts +208 -0
- package/dist/api/plugin-manifest-schema.js +23 -0
- package/dist/api/routes.d.ts +7 -0
- package/dist/api/routes.js +7 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +24 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +31 -4
- package/dist/cli.js +8 -2
- package/dist/clients/attach/overlays/graph.d.ts +5 -0
- package/dist/clients/attach/overlays/graph.js +65 -47
- package/dist/clients/attach/overlays/pickers.js +8 -9
- package/dist/clients/attach/render/card-presentation.js +1 -1
- package/dist/clients/attach/viewer.js +1286 -1260
- package/dist/commands/api-client.js +22 -23
- package/dist/commands/node/create.js +38 -3
- package/dist/commands/node/inspect.js +3 -7
- package/dist/commands/node/message.js +3 -3
- package/dist/commands/node/outcome.d.ts +2 -0
- package/dist/commands/node/outcome.js +134 -0
- package/dist/commands/node.js +2 -1
- package/dist/commands/pkg/browse/catalog.js +1 -1
- package/dist/commands/pkg/plugin-manage.d.ts +46 -1
- package/dist/commands/pkg/plugin-manage.js +170 -20
- package/dist/commands/profile/pause.js +17 -14
- package/dist/commands/push.js +102 -1
- package/dist/commands/sys/__tests__/setup-core.test.js +4 -1
- package/dist/commands/sys/panels/broker-limits-panel.js +7 -1
- package/dist/commands/sys/panels/models-panel.js +2 -2
- package/dist/commands/sys/settings-shell.d.ts +1 -1
- package/dist/commands/sys/settings-shell.js +6 -0
- package/dist/commands/sys/settings.js +3 -3
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +2 -0
- package/dist/core/__tests__/fixtures/fake-engine.js +42 -4
- package/dist/core/__tests__/helpers/harness.d.ts +1 -0
- package/dist/core/__tests__/helpers/harness.js +9 -0
- package/dist/core/__tests__/integration/plugin-revalidate.test.d.ts +1 -0
- package/dist/core/__tests__/integration/plugin-revalidate.test.js +355 -0
- package/dist/core/__tests__/integration/profile-pause.test.d.ts +1 -0
- package/dist/core/__tests__/integration/profile-pause.test.js +115 -0
- package/dist/core/__tests__/node-outcome.test.d.ts +1 -0
- package/dist/core/__tests__/node-outcome.test.js +184 -0
- package/dist/core/__tests__/revive-parked-fresh.test.js +13 -20
- package/dist/core/__tests__/seam/broker-provider-retry.test.js +6 -2
- package/dist/core/__tests__/seam/dormancy-release.test.js +3 -3
- package/dist/core/__tests__/seam/node-outcome-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/seam/node-outcome-delivery.test.js +153 -0
- package/dist/core/bash-jobs.d.ts +6 -0
- package/dist/core/bash-jobs.js +22 -0
- package/dist/core/canvas/canvas.d.ts +41 -3
- package/dist/core/canvas/canvas.js +153 -39
- package/dist/core/canvas/crons.js +2 -1
- package/dist/core/canvas/index.d.ts +1 -0
- package/dist/core/canvas/index.js +1 -0
- package/dist/core/canvas/migrations.js +47 -0
- package/dist/core/canvas/node-outcome-deliveries.d.ts +80 -0
- package/dist/core/canvas/node-outcome-deliveries.js +163 -0
- package/dist/core/canvas/pid.d.ts +7 -0
- package/dist/core/canvas/pid.js +17 -0
- package/dist/core/canvas/render-source.js +2 -2
- package/dist/core/canvas/types.d.ts +42 -1
- package/dist/core/canvas/types.js +2 -1
- package/dist/core/command-manifests/registry.d.ts +3 -1
- package/dist/core/command-manifests/schema.d.ts +2 -75
- package/dist/core/command-manifests/schema.js +5 -0
- package/dist/core/command-plugins/compose.js +1 -1
- package/dist/core/command-plugins/revalidate.d.ts +18 -0
- package/dist/core/command-plugins/revalidate.js +152 -0
- package/dist/core/command-plugins/transport/http-fetch.d.ts +29 -4
- package/dist/core/command-plugins/transport/http-fetch.js +18 -8
- package/dist/core/command-plugins/transport/http-invoke.js +7 -0
- package/dist/core/command.d.ts +8 -0
- package/dist/core/command.js +19 -3
- package/dist/core/config.d.ts +4 -1
- package/dist/core/config.js +25 -1
- package/dist/core/exclusive-lock.d.ts +12 -0
- package/dist/core/exclusive-lock.js +29 -0
- package/dist/core/feed/feed.d.ts +8 -0
- package/dist/core/feed/feed.js +25 -4
- package/dist/core/feed/inbox.d.ts +5 -0
- package/dist/core/feed/inbox.js +13 -0
- package/dist/core/installed-plugins.js +31 -0
- package/dist/core/keybindings/catalog.d.ts +1 -1
- package/dist/core/keybindings/catalog.js +3 -0
- package/dist/core/manifest-recovery.d.ts +15 -0
- package/dist/core/manifest-recovery.js +72 -0
- package/dist/core/manifest-stale.d.ts +18 -0
- package/dist/core/manifest-stale.js +39 -0
- package/dist/core/plugin-swap-lock.d.ts +9 -0
- package/dist/core/plugin-swap-lock.js +31 -0
- package/dist/core/profiles/manifest.d.ts +3 -1
- package/dist/core/profiles/manifest.js +6 -1
- package/dist/core/runtime/broker/daemon-ops.d.ts +3 -1
- package/dist/core/runtime/broker/daemon-ops.js +6 -0
- package/dist/core/runtime/broker/engine-drive.d.ts +2 -3
- package/dist/core/runtime/broker/engine-drive.js +74 -27
- package/dist/core/runtime/broker/frame-dispatch.js +1 -0
- package/dist/core/runtime/broker/read-ops.js +14 -0
- package/dist/core/runtime/broker/rebind.js +0 -7
- package/dist/core/runtime/broker-protocol.d.ts +1 -6
- package/dist/core/runtime/fleet.d.ts +7 -0
- package/dist/core/runtime/headless-pi.d.ts +13 -0
- package/dist/core/runtime/headless-pi.js +10 -2
- package/dist/core/runtime/lifecycle.d.ts +6 -1
- package/dist/core/runtime/lifecycle.js +50 -1
- package/dist/core/runtime/nodes.d.ts +5 -1
- package/dist/core/runtime/nodes.js +2 -1
- package/dist/core/runtime/outcome-document.d.ts +70 -0
- package/dist/core/runtime/outcome-document.js +97 -0
- package/dist/core/runtime/revive.js +16 -21
- package/dist/core/runtime/spawn.d.ts +5 -1
- package/dist/core/runtime/spawn.js +18 -7
- package/dist/core/runtime/stamp/channel.d.ts +3 -5
- package/dist/core/runtime/stamp/channel.js +3 -10
- package/dist/core/runtime/stop-guard.js +2 -2
- package/dist/core/runtime/structured-output.d.ts +24 -3
- package/dist/core/runtime/structured-output.js +42 -3
- package/dist/core/runtime/tmux-bindings.js +4 -0
- package/dist/core/runtime/warm-pool.d.ts +5 -2
- package/dist/core/runtime/warm-pool.js +5 -5
- package/dist/core/subscription-state.js +7 -5
- package/dist/core/substrate/render.js +10 -10
- package/dist/core/user-settings.d.ts +18 -1
- package/dist/core/user-settings.js +37 -0
- package/dist/daemon/__tests__/node-deadline-lane.test.d.ts +1 -0
- package/dist/daemon/__tests__/node-deadline-lane.test.js +76 -0
- package/dist/daemon/__tests__/node-outcome-birth-invariants.test.d.ts +1 -0
- package/dist/daemon/__tests__/node-outcome-birth-invariants.test.js +122 -0
- package/dist/daemon/__tests__/node-outcome-delivery.test.d.ts +1 -0
- package/dist/daemon/__tests__/node-outcome-delivery.test.js +86 -0
- package/dist/daemon/api/__tests__/broker-settle-park.test.js +86 -18
- package/dist/daemon/api/__tests__/profile-launch-gates.test.js +10 -1
- package/dist/daemon/api/__tests__/seam/node-outcome-api.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/seam/node-outcome-api.test.js +159 -0
- package/dist/daemon/api/__tests__/submit-result.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/submit-result.test.js +214 -0
- package/dist/daemon/api/handlers/broker-ops.js +53 -24
- package/dist/daemon/api/handlers/canvas.js +2 -2
- package/dist/daemon/api/handlers/messages.js +12 -2
- package/dist/daemon/api/handlers/node-outcomes.d.ts +2 -0
- package/dist/daemon/api/handlers/node-outcomes.js +136 -0
- package/dist/daemon/api/handlers/nodes.js +67 -1
- package/dist/daemon/api/handlers/profiles.js +53 -1
- package/dist/daemon/api/handlers/reports.js +197 -33
- package/dist/daemon/api/map.d.ts +7 -2
- package/dist/daemon/api/map.js +78 -4
- package/dist/daemon/api/server.js +2 -0
- package/dist/daemon/crtrd.js +12 -0
- package/dist/daemon/fleet.d.ts +4 -8
- package/dist/daemon/fleet.js +48 -9
- package/dist/daemon/messaging/node-message.js +2 -1
- package/dist/daemon/node-outcome/deliver-outcome.d.ts +14 -0
- package/dist/daemon/node-outcome/deliver-outcome.js +155 -0
- package/dist/daemon/park-activity.d.ts +9 -0
- package/dist/daemon/park-activity.js +30 -0
- package/dist/daemon/park-pending.d.ts +10 -5
- package/dist/daemon/park-pending.js +18 -8
- package/dist/daemon/reconcilers/bash-deadline.js +6 -0
- package/dist/daemon/reconcilers/broker-supervision.d.ts +3 -7
- package/dist/daemon/reconcilers/broker-supervision.js +29 -33
- package/dist/daemon/reconcilers/node-deadline.d.ts +5 -0
- package/dist/daemon/reconcilers/node-deadline.js +82 -0
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +9 -1
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +12 -3
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.d.ts +10 -0
- package/dist/daemon/reconcilers/node-outcome-delivery-lane.js +41 -0
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +66 -66
- package/dist/pi-extensions/canvas-structured-output.d.ts +9 -5
- package/dist/pi-extensions/canvas-structured-output.js +23 -297
- package/dist/shared/env.d.ts +3 -0
- package/dist/shared/env.js +6 -0
- package/dist/shared/generated-context.d.ts +3 -9
- package/dist/shared/generated-context.js +8 -16
- package/dist/types.d.ts +19 -0
- package/dist/types.js +1 -0
- package/package.json +9 -8
- package/runtime.lock.json +227 -383
package/dist/api/routes.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export declare const routes: {
|
|
|
8
8
|
readonly nodes: () => string;
|
|
9
9
|
readonly reviveAll: () => string;
|
|
10
10
|
readonly node: (id: string) => string;
|
|
11
|
+
readonly nodeOutcome: (id: string) => string;
|
|
12
|
+
readonly nodeOutcomeDelivery: (id: string) => string;
|
|
11
13
|
readonly nodeSnapshot: (id: string) => string;
|
|
12
14
|
readonly nodeSubject: (id: string) => string;
|
|
13
15
|
readonly nodeSession: (id: string) => string;
|
|
@@ -17,6 +19,7 @@ export declare const routes: {
|
|
|
17
19
|
readonly nodeContext: (id: string) => string;
|
|
18
20
|
readonly nodeArtifacts: (id: string) => string;
|
|
19
21
|
readonly nodeReports: (id: string) => string;
|
|
22
|
+
readonly nodeResult: (id: string) => string;
|
|
20
23
|
readonly nodeJobs: (id: string) => string;
|
|
21
24
|
readonly nodeJob: (id: string, jobId: string) => string;
|
|
22
25
|
readonly nodeMessages: (id: string) => string;
|
|
@@ -26,6 +29,8 @@ export declare const routes: {
|
|
|
26
29
|
readonly nodeRelaunchRoot: (id: string) => string;
|
|
27
30
|
readonly nodeBrokerSessionBound: (id: string) => string;
|
|
28
31
|
readonly nodeBrokerSettle: (id: string) => string;
|
|
32
|
+
readonly nodeBrokerParkComplete: (id: string) => string;
|
|
33
|
+
readonly nodeBrokerParkActivity: (id: string) => string;
|
|
29
34
|
readonly nodeBrokerInboxCursor: (id: string) => string;
|
|
30
35
|
readonly nodeBrokerModel: (id: string) => string;
|
|
31
36
|
readonly nodeBrokerExtensionState: (id: string) => string;
|
|
@@ -96,6 +101,8 @@ export declare const routes: {
|
|
|
96
101
|
readonly humanRequestCancel: (requestId: string) => string;
|
|
97
102
|
readonly profiles: () => string;
|
|
98
103
|
readonly profile: (name: string) => string;
|
|
104
|
+
readonly profilePause: (name: string) => string;
|
|
105
|
+
readonly profileResume: (name: string) => string;
|
|
99
106
|
readonly profileMetadata: (name: string) => string;
|
|
100
107
|
readonly modelAuths: () => string;
|
|
101
108
|
readonly modelAuth: (provider: string) => string;
|
package/dist/api/routes.js
CHANGED
|
@@ -23,6 +23,8 @@ export const routes = {
|
|
|
23
23
|
nodes: () => `${V}/nodes`,
|
|
24
24
|
reviveAll: () => `${V}/nodes/revive-all`,
|
|
25
25
|
node: (id) => `${V}/nodes/${id}`,
|
|
26
|
+
nodeOutcome: (id) => `${V}/nodes/${id}/outcome`,
|
|
27
|
+
nodeOutcomeDelivery: (id) => `${V}/nodes/${id}/outcome-delivery`,
|
|
26
28
|
// Node reads
|
|
27
29
|
nodeSnapshot: (id) => `${V}/nodes/${id}/snapshot`,
|
|
28
30
|
nodeSubject: (id) => `${V}/nodes/${id}/subject`,
|
|
@@ -33,6 +35,7 @@ export const routes = {
|
|
|
33
35
|
nodeContext: (id) => `${V}/nodes/${id}/context`,
|
|
34
36
|
nodeArtifacts: (id) => `${V}/nodes/${id}/artifacts`,
|
|
35
37
|
nodeReports: (id) => `${V}/nodes/${id}/reports`,
|
|
38
|
+
nodeResult: (id) => `${V}/nodes/${id}/result`,
|
|
36
39
|
nodeJobs: (id) => `${V}/nodes/${id}/jobs`,
|
|
37
40
|
nodeJob: (id, jobId) => `${V}/nodes/${id}/jobs/${jobId}`,
|
|
38
41
|
// Node messages / feed
|
|
@@ -44,6 +47,8 @@ export const routes = {
|
|
|
44
47
|
nodeRelaunchRoot: (id) => `${V}/nodes/${id}/relaunch-root`,
|
|
45
48
|
nodeBrokerSessionBound: (id) => `${V}/nodes/${id}/broker/session-bound`,
|
|
46
49
|
nodeBrokerSettle: (id) => `${V}/nodes/${id}/broker/settle`,
|
|
50
|
+
nodeBrokerParkComplete: (id) => `${V}/nodes/${id}/broker/park-complete`,
|
|
51
|
+
nodeBrokerParkActivity: (id) => `${V}/nodes/${id}/broker/park-activity`,
|
|
47
52
|
nodeBrokerInboxCursor: (id) => `${V}/nodes/${id}/broker/inbox-cursor`,
|
|
48
53
|
nodeBrokerModel: (id) => `${V}/nodes/${id}/broker/model`,
|
|
49
54
|
nodeBrokerExtensionState: (id) => `${V}/nodes/${id}/broker/extension-state`,
|
|
@@ -127,6 +132,8 @@ export const routes = {
|
|
|
127
132
|
// Profiles (deletion is daemon-owned because it crosses canvas state)
|
|
128
133
|
profiles: () => `${V}/profiles`,
|
|
129
134
|
profile: (name) => `${V}/profiles/${name}`,
|
|
135
|
+
profilePause: (name) => `${V}/profiles/${name}/pause`,
|
|
136
|
+
profileResume: (name) => `${V}/profiles/${name}/resume`,
|
|
130
137
|
profileMetadata: (name) => `${V}/profiles/${name}/metadata`,
|
|
131
138
|
// Model auth
|
|
132
139
|
modelAuths: () => `${V}/model-auth`,
|
|
@@ -405,6 +405,18 @@ async function streamProviderAttempt(state, providerId, model, context, options,
|
|
|
405
405
|
fetch: withAnthropicFallbackBeta(options?.fetch ?? globalThis.fetch),
|
|
406
406
|
}
|
|
407
407
|
: {}),
|
|
408
|
+
// Codex fast mode: the ChatGPT backend serves the turn at its priority
|
|
409
|
+
// tier. The wire value is `priority` (`fast` is rejected 400), and the
|
|
410
|
+
// response always echoes `service_tier: "default"` regardless — only
|
|
411
|
+
// generation timing shows whether it took effect.
|
|
412
|
+
...(providerId === OPENAI_CODEX_PROVIDER_ID && state.providerOptions?.[OPENAI_CODEX_PROVIDER_ID]?.fastMode === true
|
|
413
|
+
? {
|
|
414
|
+
onPayload: async (payload, payloadModel) => {
|
|
415
|
+
const callerPayload = await options?.onPayload?.(payload, payloadModel);
|
|
416
|
+
return { ...(callerPayload === undefined ? payload : callerPayload), service_tier: "priority" };
|
|
417
|
+
},
|
|
418
|
+
}
|
|
419
|
+
: {}),
|
|
408
420
|
onResponse: async (response, responseModel) => {
|
|
409
421
|
observedStatus = response.status;
|
|
410
422
|
if (response.status === 429 || response.status === 503) {
|
|
@@ -773,8 +785,12 @@ function parseCommand(args) {
|
|
|
773
785
|
function parseProvider(value) {
|
|
774
786
|
return isManagedProvider(value) ? value : undefined;
|
|
775
787
|
}
|
|
776
|
-
|
|
777
|
-
|
|
788
|
+
/** Merged config for the NODE's scope (its own cwd/profile), not this
|
|
789
|
+
* process's ambient one. */
|
|
790
|
+
function readTargetLaunchConfig(state, ctx) {
|
|
791
|
+
return readMergedLaunchConfig(ctx.cwd, configuredValue(state, 'profileId', 'CRTR_PROFILE_ID') || null);
|
|
792
|
+
}
|
|
793
|
+
function installDeclaredRouteProviders(state, pi, ctx, config) {
|
|
778
794
|
const providerIds = new Set(Object.values({ ...(config.modelRoutes ?? {}), ...(config.legacyModelRoutes ?? {}) }).map((route) => route.providerId));
|
|
779
795
|
for (const providerId of providerIds) {
|
|
780
796
|
if (isManagedProvider(providerId) || state.delegatedProviderStreams.has(providerId))
|
|
@@ -931,11 +947,16 @@ export function createProviderRotationExtension(options) {
|
|
|
931
947
|
});
|
|
932
948
|
pi.on("session_start", async (_event, ctx) => {
|
|
933
949
|
state.runtimeContext = ctx;
|
|
934
|
-
|
|
950
|
+
const config = readTargetLaunchConfig(state, ctx);
|
|
951
|
+
state.providerOptions = config.providerOptions;
|
|
952
|
+
installDeclaredRouteProviders(state, pi, ctx, config);
|
|
935
953
|
updateRotationStatus(state, ctx);
|
|
936
954
|
});
|
|
937
955
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
938
956
|
state.runtimeContext = ctx;
|
|
957
|
+
// Re-read per turn: this is what makes a provider-options toggle apply to
|
|
958
|
+
// a broker that is already running, on its next turn.
|
|
959
|
+
state.providerOptions = readTargetLaunchConfig(state, ctx).providerOptions;
|
|
939
960
|
const restored = await restoreHigherRankIfAvailable(state, pi, ctx);
|
|
940
961
|
if (restored && ctx.model && isManagedProvider(ctx.model.provider)) {
|
|
941
962
|
logRotation(`restored higher-ranked route ${ctx.model.provider}/${ctx.model.id}`);
|
|
@@ -37,7 +37,8 @@ import {
|
|
|
37
37
|
type ManagedProviderId,
|
|
38
38
|
type SubscriptionCredential,
|
|
39
39
|
} from '../../../core/subscription-state.js';
|
|
40
|
-
import { readMergedLaunchConfig } from '../../../core/config.js';
|
|
40
|
+
import { readMergedLaunchConfig, type MergedLaunchConfig } from '../../../core/config.js';
|
|
41
|
+
import type { ProviderOptionsConfig } from '../../../types.js';
|
|
41
42
|
import { expandModelCandidates, modelRequestFromConfig, probeRouteAvailability } from '../../../core/model-routes.js';
|
|
42
43
|
import { envModelExact, envNodeId } from '../../../shared/env.js';
|
|
43
44
|
import { ManagedProviderCoolingError } from '../../../core/runtime/managed-provider-cooling.js';
|
|
@@ -83,6 +84,10 @@ type ProviderRotationState = {
|
|
|
83
84
|
extensionAPI?: ExtensionAPI;
|
|
84
85
|
delegatedProviderStreams: Map<string, ProviderStream>;
|
|
85
86
|
options?: Readonly<ProviderRotationOptions>;
|
|
87
|
+
/** Per-provider behavior options from merged config, refreshed at session
|
|
88
|
+
* start and before every agent turn so a settings toggle reaches a broker
|
|
89
|
+
* that is already live. */
|
|
90
|
+
providerOptions?: Record<string, ProviderOptionsConfig>;
|
|
86
91
|
/** Consecutive route-queue passes that exhausted every route without a single
|
|
87
92
|
* HTTP response — the poisoned-process signature counted across turns. */
|
|
88
93
|
transportOnlyExhaustedPasses: number;
|
|
@@ -563,6 +568,18 @@ async function streamProviderAttempt(
|
|
|
563
568
|
fetch: withAnthropicFallbackBeta(options?.fetch ?? globalThis.fetch),
|
|
564
569
|
}
|
|
565
570
|
: {}),
|
|
571
|
+
// Codex fast mode: the ChatGPT backend serves the turn at its priority
|
|
572
|
+
// tier. The wire value is `priority` (`fast` is rejected 400), and the
|
|
573
|
+
// response always echoes `service_tier: "default"` regardless — only
|
|
574
|
+
// generation timing shows whether it took effect.
|
|
575
|
+
...(providerId === OPENAI_CODEX_PROVIDER_ID && state.providerOptions?.[OPENAI_CODEX_PROVIDER_ID]?.fastMode === true
|
|
576
|
+
? {
|
|
577
|
+
onPayload: async (payload: unknown, payloadModel: Model<any>) => {
|
|
578
|
+
const callerPayload = await options?.onPayload?.(payload, payloadModel);
|
|
579
|
+
return { ...((callerPayload === undefined ? payload : callerPayload) as Record<string, unknown>), service_tier: "priority" };
|
|
580
|
+
},
|
|
581
|
+
}
|
|
582
|
+
: {}),
|
|
566
583
|
onResponse: async (response: { status: number; headers: Record<string, string> }, responseModel: Model<any>) => {
|
|
567
584
|
observedStatus = response.status;
|
|
568
585
|
if (response.status === 429 || response.status === 503) {
|
|
@@ -957,8 +974,13 @@ function parseProvider(value: string | undefined): ManagedProviderId | undefined
|
|
|
957
974
|
return isManagedProvider(value) ? value : undefined;
|
|
958
975
|
}
|
|
959
976
|
|
|
960
|
-
|
|
961
|
-
|
|
977
|
+
/** Merged config for the NODE's scope (its own cwd/profile), not this
|
|
978
|
+
* process's ambient one. */
|
|
979
|
+
function readTargetLaunchConfig(state: ProviderRotationState, ctx: ExtensionContext): MergedLaunchConfig {
|
|
980
|
+
return readMergedLaunchConfig(ctx.cwd, configuredValue(state, 'profileId', 'CRTR_PROFILE_ID') || null);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function installDeclaredRouteProviders(state: ProviderRotationState, pi: ExtensionAPI, ctx: ExtensionContext, config: MergedLaunchConfig): void {
|
|
962
984
|
const providerIds = new Set(Object.values({ ...(config.modelRoutes ?? {}), ...(config.legacyModelRoutes ?? {}) }).map((route) => route.providerId));
|
|
963
985
|
for (const providerId of providerIds) {
|
|
964
986
|
if (isManagedProvider(providerId) || state.delegatedProviderStreams.has(providerId)) continue;
|
|
@@ -1129,12 +1151,17 @@ export function createProviderRotationExtension(options?: ProviderRotationOption
|
|
|
1129
1151
|
|
|
1130
1152
|
pi.on("session_start", async (_event, ctx) => {
|
|
1131
1153
|
state.runtimeContext = ctx;
|
|
1132
|
-
|
|
1154
|
+
const config = readTargetLaunchConfig(state, ctx);
|
|
1155
|
+
state.providerOptions = config.providerOptions;
|
|
1156
|
+
installDeclaredRouteProviders(state, pi, ctx, config);
|
|
1133
1157
|
updateRotationStatus(state, ctx);
|
|
1134
1158
|
});
|
|
1135
1159
|
|
|
1136
1160
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
1137
1161
|
state.runtimeContext = ctx;
|
|
1162
|
+
// Re-read per turn: this is what makes a provider-options toggle apply to
|
|
1163
|
+
// a broker that is already running, on its next turn.
|
|
1164
|
+
state.providerOptions = readTargetLaunchConfig(state, ctx).providerOptions;
|
|
1138
1165
|
const restored = await restoreHigherRankIfAvailable(state, pi, ctx);
|
|
1139
1166
|
if (restored && ctx.model && isManagedProvider(ctx.model.provider)) {
|
|
1140
1167
|
logRotation(`restored higher-ranked route ${ctx.model.provider}/${ctx.model.id}`);
|
package/dist/cli.js
CHANGED
|
@@ -3,12 +3,13 @@ import './suppress-experimental-warnings.js';
|
|
|
3
3
|
import { openSync } from 'node:fs';
|
|
4
4
|
import { enableCompileCache } from 'node:module';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { runCli } from './core/command.js';
|
|
7
6
|
import { resolveRoot } from './build-root.js';
|
|
8
7
|
import { maybeBootRoot } from './core/runtime/front-door.js';
|
|
9
8
|
import { maybeAutoUpdate } from './core/auto-update.js';
|
|
10
9
|
import { ensureProjectScope } from './core/bootstrap.js';
|
|
11
10
|
import { provisionExports, pruneLegacyExports } from './core/host-exports/export.js';
|
|
11
|
+
import { revalidateBundlePlugins } from './core/command-plugins/revalidate.js';
|
|
12
|
+
import { dispatchWithStaleRecovery } from './core/manifest-recovery.js';
|
|
12
13
|
import { installStdoutErrorPolicy } from './core/stdout-errors.js';
|
|
13
14
|
import { handle } from './core/io.js';
|
|
14
15
|
import { CrtrError } from './core/errors.js';
|
|
@@ -46,6 +47,11 @@ async function main() {
|
|
|
46
47
|
// bootRoot exec'd pi inline and exited; unreachable.
|
|
47
48
|
}
|
|
48
49
|
mark('cli.front_door_passed');
|
|
50
|
+
// Bring installed bundle plugins back in step with their endpoints before the
|
|
51
|
+
// tree is built, so a package this pass replaces is the one this invocation
|
|
52
|
+
// dispatches against. TTL-gated and non-fatal — see revalidate.ts.
|
|
53
|
+
await revalidateBundlePlugins(process.argv);
|
|
54
|
+
mark('cli.plugins_revalidated');
|
|
49
55
|
// Lazy command tree: load only the subtree this invocation dispatches into.
|
|
50
56
|
// resolveRoot returns the full tree only for help/version/unknown, where
|
|
51
57
|
// every subtree is genuinely needed (root -h, the unknown-path error). The
|
|
@@ -57,7 +63,7 @@ async function main() {
|
|
|
57
63
|
ensureProjectScope(process.argv);
|
|
58
64
|
maybeAutoUpdate(process.argv);
|
|
59
65
|
mark('cli.dispatch_start');
|
|
60
|
-
await
|
|
66
|
+
await dispatchWithStaleRecovery(root, process.argv);
|
|
61
67
|
mark('cli.dispatch_done');
|
|
62
68
|
}
|
|
63
69
|
main().catch((err) => {
|
|
@@ -54,6 +54,11 @@ export declare class GraphOverlay implements Component {
|
|
|
54
54
|
refresh(): void;
|
|
55
55
|
invalidate(): void;
|
|
56
56
|
private buildSnapshot;
|
|
57
|
+
/** Fold state is presentation-only. Reproject the visible DFS from the fully
|
|
58
|
+
* fetched topology so h/l never cross the daemon boundary. The explicit stack
|
|
59
|
+
* also keeps a deep graph from consuming the JavaScript call stack. */
|
|
60
|
+
private projectRows;
|
|
61
|
+
private repaintFolds;
|
|
57
62
|
private renderSnapshotRow;
|
|
58
63
|
render(width: number): string[];
|
|
59
64
|
/** A top/bottom border row: `<lc>─ <label> ────<rc>`, frame in the theme border
|
|
@@ -205,24 +205,15 @@ export class GraphOverlay {
|
|
|
205
205
|
}
|
|
206
206
|
async buildSnapshot() {
|
|
207
207
|
beginFrameTelem();
|
|
208
|
-
const
|
|
208
|
+
const [listed, focused] = await Promise.all([
|
|
209
|
+
this.source.listNodes().catch(() => []),
|
|
210
|
+
this.fetchFocused(),
|
|
211
|
+
]);
|
|
212
|
+
const nodes = new Map(listed.map((node) => [node.node_id, node]));
|
|
209
213
|
const childIds = new Map();
|
|
210
214
|
const sortedChildIds = new Map();
|
|
211
215
|
const managerOf = new Map();
|
|
212
216
|
const activeBelow = new Map();
|
|
213
|
-
const node = async (id) => {
|
|
214
|
-
if (nodes.has(id))
|
|
215
|
-
return nodes.get(id) ?? null;
|
|
216
|
-
let v;
|
|
217
|
-
try {
|
|
218
|
-
v = await this.source.getNode(id);
|
|
219
|
-
}
|
|
220
|
-
catch {
|
|
221
|
-
v = null;
|
|
222
|
-
}
|
|
223
|
-
nodes.set(id, v);
|
|
224
|
-
return v;
|
|
225
|
-
};
|
|
226
217
|
const children = async (id) => {
|
|
227
218
|
const hit = childIds.get(id);
|
|
228
219
|
if (hit !== undefined)
|
|
@@ -234,11 +225,9 @@ export class GraphOverlay {
|
|
|
234
225
|
catch {
|
|
235
226
|
refs = [];
|
|
236
227
|
}
|
|
237
|
-
const out =
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
out.push(ref.node_id);
|
|
241
|
-
}
|
|
228
|
+
const out = refs
|
|
229
|
+
.map((ref) => ref.node_id)
|
|
230
|
+
.filter((childId) => nodes.get(childId)?.kind !== 'human');
|
|
242
231
|
childIds.set(id, out);
|
|
243
232
|
return out;
|
|
244
233
|
};
|
|
@@ -255,8 +244,8 @@ export class GraphOverlay {
|
|
|
255
244
|
managerOf.set(id, mgr);
|
|
256
245
|
return mgr;
|
|
257
246
|
};
|
|
258
|
-
const climbRoot = async (
|
|
259
|
-
let cur = self;
|
|
247
|
+
const climbRoot = async () => {
|
|
248
|
+
let cur = this.self;
|
|
260
249
|
const seen = new Set([cur]);
|
|
261
250
|
for (;;) {
|
|
262
251
|
const mgr = await manager(cur);
|
|
@@ -267,60 +256,89 @@ export class GraphOverlay {
|
|
|
267
256
|
}
|
|
268
257
|
return cur;
|
|
269
258
|
};
|
|
270
|
-
const rank = async (id) => statusRank((await node(id))?.status);
|
|
271
259
|
const sortedChildren = async (id) => {
|
|
272
260
|
const cached = sortedChildIds.get(id);
|
|
273
261
|
if (cached !== undefined)
|
|
274
262
|
return cached;
|
|
275
263
|
const ids = [...await children(id)];
|
|
276
|
-
|
|
277
|
-
await Promise.all(ids.map(async (cid) => { ranks.set(cid, await rank(cid)); }));
|
|
278
|
-
ids.sort((a, b) => (ranks.get(a) ?? 99) - (ranks.get(b) ?? 99));
|
|
264
|
+
ids.sort((a, b) => statusRank(nodes.get(a)?.status) - statusRank(nodes.get(b)?.status));
|
|
279
265
|
sortedChildIds.set(id, ids);
|
|
280
266
|
return ids;
|
|
281
267
|
};
|
|
282
|
-
const
|
|
268
|
+
const autoExpanded = new Set();
|
|
283
269
|
const seenActivity = new Set();
|
|
284
270
|
const visitActivity = async (id) => {
|
|
285
|
-
const selfActive = (
|
|
271
|
+
const selfActive = nodes.get(id)?.status === 'active';
|
|
286
272
|
if (seenActivity.has(id))
|
|
287
273
|
return { active: selfActive ? 1 : 0, reveal: selfActive || id === this.self };
|
|
288
274
|
seenActivity.add(id);
|
|
289
275
|
let below = 0;
|
|
290
276
|
let childReveal = false;
|
|
291
|
-
for (const
|
|
292
|
-
const
|
|
293
|
-
below +=
|
|
294
|
-
if (
|
|
277
|
+
for (const childId of await children(id)) {
|
|
278
|
+
const result = await visitActivity(childId);
|
|
279
|
+
below += result.active;
|
|
280
|
+
if (result.reveal)
|
|
295
281
|
childReveal = true;
|
|
296
282
|
}
|
|
297
283
|
activeBelow.set(id, below);
|
|
298
284
|
if (childReveal)
|
|
299
|
-
|
|
285
|
+
autoExpanded.add(id);
|
|
300
286
|
return { active: below + (selfActive ? 1 : 0), reveal: childReveal || selfActive || id === this.self };
|
|
301
287
|
};
|
|
302
|
-
|
|
288
|
+
const root = await climbRoot();
|
|
289
|
+
await visitActivity(root);
|
|
290
|
+
await Promise.all([...childIds.keys()].map((id) => sortedChildren(id)));
|
|
291
|
+
const snapshot = {
|
|
292
|
+
root,
|
|
293
|
+
rows: [],
|
|
294
|
+
autoExpanded,
|
|
295
|
+
activeBelow,
|
|
296
|
+
nodes,
|
|
297
|
+
childIds,
|
|
298
|
+
sortedChildIds,
|
|
299
|
+
managerOf,
|
|
300
|
+
focused,
|
|
301
|
+
};
|
|
302
|
+
snapshot.rows = this.projectRows(snapshot);
|
|
303
|
+
return snapshot;
|
|
304
|
+
}
|
|
305
|
+
/** Fold state is presentation-only. Reproject the visible DFS from the fully
|
|
306
|
+
* fetched topology so h/l never cross the daemon boundary. The explicit stack
|
|
307
|
+
* also keeps a deep graph from consuming the JavaScript call stack. */
|
|
308
|
+
projectRows(snapshot) {
|
|
303
309
|
const rows = [];
|
|
304
310
|
const visited = new Set();
|
|
305
|
-
const
|
|
311
|
+
const stack = [
|
|
312
|
+
{ id: snapshot.root, prefix: '', isRoot: true, isLast: true },
|
|
313
|
+
];
|
|
314
|
+
while (stack.length > 0) {
|
|
315
|
+
const { id, prefix, isRoot, isLast } = stack.pop();
|
|
316
|
+
const connector = isRoot ? '' : isLast ? '└─ ' : '├─ ';
|
|
306
317
|
if (visited.has(id)) {
|
|
307
|
-
const connector = isRoot ? '' : isLast ? '└─ ' : '├─ ';
|
|
308
318
|
rows.push({ id, hasKids: false, isSelf: id === this.self, branch: prefix + connector, cycle: true, collapsed: false });
|
|
309
|
-
|
|
319
|
+
continue;
|
|
310
320
|
}
|
|
311
321
|
visited.add(id);
|
|
312
|
-
const kids =
|
|
313
|
-
const folded = this.folds.userExpanded.has(id)
|
|
314
|
-
|
|
322
|
+
const kids = snapshot.sortedChildIds.get(id) ?? [];
|
|
323
|
+
const folded = this.folds.userExpanded.has(id)
|
|
324
|
+
? false
|
|
325
|
+
: this.folds.userCollapsed.has(id)
|
|
326
|
+
? true
|
|
327
|
+
: !snapshot.autoExpanded.has(id);
|
|
315
328
|
rows.push({ id, hasKids: kids.length > 0, isSelf: id === this.self, branch: prefix + connector, cycle: false, collapsed: folded });
|
|
316
329
|
if (folded)
|
|
317
|
-
|
|
330
|
+
continue;
|
|
318
331
|
const childPrefix = isRoot ? '' : prefix + (isLast ? ' ' : '│ ');
|
|
319
|
-
for (let i =
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
return
|
|
332
|
+
for (let i = kids.length - 1; i >= 0; i--) {
|
|
333
|
+
stack.push({ id: kids[i], prefix: childPrefix, isRoot: false, isLast: i === kids.length - 1 });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return rows;
|
|
337
|
+
}
|
|
338
|
+
repaintFolds() {
|
|
339
|
+
if (this.snapshot !== undefined)
|
|
340
|
+
this.snapshot.rows = this.projectRows(this.snapshot);
|
|
341
|
+
this.tui.requestRender();
|
|
324
342
|
}
|
|
325
343
|
renderSnapshotRow(snapshot, r, isCursor, asks) {
|
|
326
344
|
const wrap = (line, attached) => isCursor ? fillBar(line, fillWidth(), REVERSE)
|
|
@@ -523,7 +541,7 @@ export class GraphOverlay {
|
|
|
523
541
|
if (cur !== undefined && cur.hasKids && !cur.collapsed) {
|
|
524
542
|
this.userCollapsed.add(cur.id);
|
|
525
543
|
this.userExpanded.delete(cur.id);
|
|
526
|
-
this.
|
|
544
|
+
this.repaintFolds();
|
|
527
545
|
}
|
|
528
546
|
else {
|
|
529
547
|
const p = snapshot?.managerOf.get(this.cursorId ?? this.self);
|
|
@@ -537,7 +555,7 @@ export class GraphOverlay {
|
|
|
537
555
|
if (cur !== undefined && cur.collapsed && cur.hasKids) {
|
|
538
556
|
this.userExpanded.add(cur.id);
|
|
539
557
|
this.userCollapsed.delete(cur.id);
|
|
540
|
-
this.
|
|
558
|
+
this.repaintFolds();
|
|
541
559
|
}
|
|
542
560
|
else if (cur !== undefined && cur.hasKids) {
|
|
543
561
|
const c = snapshot?.sortedChildIds.get(cur.id)?.[0];
|
|
@@ -14,20 +14,19 @@
|
|
|
14
14
|
//
|
|
15
15
|
// R1 (contract): the tree/fork/settings payloads are PURE DATA — their selectors
|
|
16
16
|
// build verbatim, no reconstruction. The MODEL picker's `ModelSelectorComponent`
|
|
17
|
-
// needs a live `ModelRuntime
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// crossing engine state over the socket. The SESSION picker reconstructs
|
|
17
|
+
// needs a live `ModelRuntime`: we feed the REAL component a tiny `ModelRuntime`
|
|
18
|
+
// adapter (the component calls only getAvailableSnapshot/getModel/getError —
|
|
19
|
+
// verified against model-selector.js), preserving its full search + scope-toggle
|
|
20
|
+
// UI without crossing engine state over the socket. The SESSION picker reconstructs
|
|
22
21
|
// faithfully from async loaders (no shim). The SCOPED-MODELS component is NOT
|
|
23
22
|
// re-exported by pi (through 0.79.1) AND has no mutation frame in the contract,
|
|
24
23
|
// so it renders read-only via a pi-tui SelectList.
|
|
25
24
|
//
|
|
26
|
-
// The duck-typed runtime adapter relies on the component's current
|
|
27
|
-
//
|
|
25
|
+
// The duck-typed runtime adapter relies on the component's current ModelRuntime
|
|
26
|
+
// surface — a future pi adding a runtime call there would surface at
|
|
28
27
|
// runtime (caught by live verification).
|
|
29
28
|
import { Container, SelectList, SettingsList, } from '@earendil-works/pi-tui';
|
|
30
|
-
import { DynamicBorder, getSelectListTheme, getSettingsListTheme, ModelSelectorComponent, SessionSelectorComponent, TreeSelectorComponent, UserMessageSelectorComponent,
|
|
29
|
+
import { DynamicBorder, getSelectListTheme, getSettingsListTheme, ModelSelectorComponent, SessionSelectorComponent, TreeSelectorComponent, UserMessageSelectorComponent, } from '@earendil-works/pi-coding-agent';
|
|
31
30
|
/** A minimal `ModelRuntime` for `ModelSelectorComponent`: the component calls
|
|
32
31
|
* only `getAvailableSnapshot()`, `getModel()`, `getError()`, and `refresh()`
|
|
33
32
|
* (verified against pi `model-selector.js`). We satisfy exactly those from the
|
|
@@ -62,7 +61,7 @@ export function buildModelPicker(tui, data, send, close) {
|
|
|
62
61
|
const current = data.current
|
|
63
62
|
? all.find((m) => m.provider === data.current.provider && m.id === data.current.id)
|
|
64
63
|
: undefined;
|
|
65
|
-
const component = new ModelSelectorComponent(tui, current,
|
|
64
|
+
const component = new ModelSelectorComponent(tui, current, modelRuntimeAdapter(all, available), data.scopedModels, (model) => {
|
|
66
65
|
send({ type: 'set_model', model: `${model.provider}/${model.id}` });
|
|
67
66
|
close();
|
|
68
67
|
}, () => close());
|
|
@@ -102,7 +102,7 @@ function reviewCommentSummary(card) {
|
|
|
102
102
|
}
|
|
103
103
|
/** Both structured-output reprompts open this way (core/runtime/stop-guard.ts);
|
|
104
104
|
* the stall reprompt does not. */
|
|
105
|
-
const STRUCTURED_OUTPUT_REPROMPT = /^(?:You must
|
|
105
|
+
const STRUCTURED_OUTPUT_REPROMPT = /^(?:You must submit a result|Structured-output request file is invalid)/;
|
|
106
106
|
function stopGuardSummary(card) {
|
|
107
107
|
return STRUCTURED_OUTPUT_REPROMPT.test(card.body) ? 'structured output required' : 'completion required';
|
|
108
108
|
}
|