@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
|
@@ -33,7 +33,6 @@ import { InputError } from '../io.js';
|
|
|
33
33
|
import { buildPiArgv } from './launch.js';
|
|
34
34
|
import { buildReviveKickoff, drainBearings, readGoal, readYieldMessage, RUNTIME_RESTART_CONTINUATION } from './kickoff.js';
|
|
35
35
|
import { readRoadmap } from './roadmap.js';
|
|
36
|
-
import { hasUnseenWakeMail } from '../feed/inbox.js';
|
|
37
36
|
import { encodeKickoffOrigin, KICKOFF_ORIGIN_ENV } from './stamp/protocol.js';
|
|
38
37
|
import { headlessBrokerHost } from './host.js';
|
|
39
38
|
import { boundFleet, brokerCapReached, brokerThresholdsForDaemon } from './fleet.js';
|
|
@@ -324,26 +323,14 @@ async function launchRevive(nodeId, meta, opts) {
|
|
|
324
323
|
updateNode(nodeId, { cycles: meta.cycles });
|
|
325
324
|
// Ordinary nodes choose resume/cycle/fresh from their recorded session.
|
|
326
325
|
// Persisted pending-refresh state (durable across a pre-session_start
|
|
327
|
-
// crash-retry) outranks a caller's strict-resume request.
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
// window is for: every doorway that wakes a parked node because something
|
|
334
|
-
// arrived appends the durable entry BEFORE calling in, so the unseen set IS
|
|
335
|
-
// that input. An input-less revival — a bare `node lifecycle revive`, a viewer
|
|
336
|
-
// attach, a focus — has nothing to answer, so a fresh cycle would only buy a
|
|
337
|
-
// nothing-to-do turn and a re-park 15 minutes later: it quietly resumes the
|
|
338
|
-
// old transcript instead, injects no kickoff, and generates nothing. The park
|
|
339
|
-
// clock re-parks it in due course.
|
|
340
|
-
// The second exception is unchanged: a park with nothing on disk to ground a
|
|
341
|
-
// fresh window (reachable only on the degraded path, which writes no summary)
|
|
342
|
-
// resumes rather than waking amnesiac.
|
|
343
|
-
// The transition below clears the marker, and this snapshot precedes it.
|
|
326
|
+
// crash-retry) outranks a caller's strict-resume request. A parked node now
|
|
327
|
+
// resumes its transcript too: its isolated parking turn never entered the
|
|
328
|
+
// main session, so wake mail continues one intact conversation without a
|
|
329
|
+
// fresh-cycle handoff. resumeArgs then selects only an existing absolute
|
|
330
|
+
// session path: strict resume continues its leaf, while refresh/cycle mode
|
|
331
|
+
// roots a new one.
|
|
344
332
|
const refreshPending = meta.intent === 'refresh' || meta.cycle_pending === true;
|
|
345
|
-
const
|
|
346
|
-
const effectiveResume = refreshPending || parkedReentry ? false : opts.resume;
|
|
333
|
+
const effectiveResume = refreshPending ? false : opts.resume;
|
|
347
334
|
const resume = forkBirthPending ? {} : resumableSessionArgs(nodeId, meta, effectiveResume);
|
|
348
335
|
// hasSessionPath: there is a real `.jsonl` to hand pi via `--session`, whether
|
|
349
336
|
// as a true resume OR a cycling refresh-yield (resumeArgs sets `newCycle` in
|
|
@@ -503,7 +490,15 @@ async function launchRevive(nodeId, meta, opts) {
|
|
|
503
490
|
}
|
|
504
491
|
catch (error) {
|
|
505
492
|
try {
|
|
506
|
-
transition(nodeId, 'crash'
|
|
493
|
+
transition(nodeId, 'crash', {
|
|
494
|
+
reason: 'launch_failed',
|
|
495
|
+
outcome: {
|
|
496
|
+
detail: {
|
|
497
|
+
schema: 'crtr.node-outcome-detail/v1',
|
|
498
|
+
message: error instanceof Error ? error.message : String(error),
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
});
|
|
507
502
|
// Doctrine wake: a launch-refusal here marks the node dead with NOTHING
|
|
508
503
|
// else fanning that out — the daemon's own auto-revive is the most common
|
|
509
504
|
// caller (superviseTick, best-effort try/catch, stderr-only on failure), so
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type WakeOrigin } from './bearings.js';
|
|
2
|
-
import { type NodeMeta, type Mode, type Lifecycle } from '../canvas/index.js';
|
|
2
|
+
import { type NodeMeta, type Mode, type Lifecycle, type OutcomeDeliveryBinding } from '../canvas/index.js';
|
|
3
3
|
import { type ModelRegistryLike, type ModelRequest } from '../model-routes.js';
|
|
4
4
|
export interface SpawnChildOpts {
|
|
5
5
|
kind: string;
|
|
@@ -62,6 +62,10 @@ export interface SpawnChildOpts {
|
|
|
62
62
|
worktree?: boolean;
|
|
63
63
|
/** Local branch the managed worktree is cut from and later landed onto. */
|
|
64
64
|
worktreeBase?: string;
|
|
65
|
+
/** Absolute wall-clock deadline committed with this birth. */
|
|
66
|
+
deadlineAt?: string;
|
|
67
|
+
/** Delivery target committed with this birth before the broker launches. */
|
|
68
|
+
outcomeDelivery?: OutcomeDeliveryBinding;
|
|
65
69
|
/** Invocation-selected repository from which to cut a managed worktree. */
|
|
66
70
|
worktreeCwd?: string;
|
|
67
71
|
/** Preallocated node id, used when a managed worktree must be named before birth. */
|
|
@@ -40,13 +40,21 @@ import { MODEL_SPEC_NEXT } from './model-selection.js';
|
|
|
40
40
|
import { initializeForkContextExposure } from '../substrate/injected-store.js';
|
|
41
41
|
// Shared broker-launch wrapper — catch launch/preflight failures at the call
|
|
42
42
|
// site, mark the node crashed, then rethrow a clear error.
|
|
43
|
+
function launchFailureOutcome(error) {
|
|
44
|
+
return {
|
|
45
|
+
detail: {
|
|
46
|
+
schema: 'crtr.node-outcome-detail/v1',
|
|
47
|
+
message: error instanceof Error ? error.message : String(error),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
43
51
|
function launchBrokerOrCrash(nodeId, inv, opts) {
|
|
44
52
|
try {
|
|
45
53
|
return headlessBrokerHost.launch(nodeId, inv, opts);
|
|
46
54
|
}
|
|
47
55
|
catch (error) {
|
|
48
56
|
try {
|
|
49
|
-
transition(nodeId, 'crash');
|
|
57
|
+
transition(nodeId, 'crash', { reason: 'launch_failed', outcome: launchFailureOutcome(error) });
|
|
50
58
|
}
|
|
51
59
|
catch {
|
|
52
60
|
/* best-effort cleanup */
|
|
@@ -63,10 +71,10 @@ function launchBrokerOrCrash(nodeId, inv, opts) {
|
|
|
63
71
|
}
|
|
64
72
|
}
|
|
65
73
|
/** A readiness failure must never leave a broker able to bind behind a dead row. */
|
|
66
|
-
function crashAndTearDown(nodeId) {
|
|
74
|
+
function crashAndTearDown(nodeId, error) {
|
|
67
75
|
try {
|
|
68
76
|
// Mark dead before touching the process so daemon supervision cannot revive it.
|
|
69
|
-
transition(nodeId, 'crash');
|
|
77
|
+
transition(nodeId, 'crash', { reason: 'launch_failed', outcome: launchFailureOutcome(error) });
|
|
70
78
|
}
|
|
71
79
|
catch {
|
|
72
80
|
/* preserve teardown even if lifecycle reconciliation already happened */
|
|
@@ -288,6 +296,8 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
288
296
|
modelOverride: opts.model === undefined ? undefined : launch.model,
|
|
289
297
|
...(managedWorktree !== undefined ? { managedWorktree } : {}),
|
|
290
298
|
...(nodeId !== undefined ? { nodeId } : {}),
|
|
299
|
+
...(opts.deadlineAt === undefined ? {} : { deadlineAt: opts.deadlineAt }),
|
|
300
|
+
...(opts.outcomeDelivery === undefined ? {} : { outcomeDelivery: opts.outcomeDelivery }),
|
|
291
301
|
launch,
|
|
292
302
|
});
|
|
293
303
|
if (opts.forkFrom !== undefined) {
|
|
@@ -295,7 +305,7 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
295
305
|
initializeForkContextExposure(meta.node_id, forkSourceNodeId);
|
|
296
306
|
}
|
|
297
307
|
catch (error) {
|
|
298
|
-
transition(meta.node_id, 'crash');
|
|
308
|
+
transition(meta.node_id, 'crash', { reason: 'launch_failed', outcome: launchFailureOutcome(error) });
|
|
299
309
|
throw error;
|
|
300
310
|
}
|
|
301
311
|
}
|
|
@@ -315,7 +325,7 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
315
325
|
beforeBrokerLaunch(meta.node_id);
|
|
316
326
|
}
|
|
317
327
|
catch (error) {
|
|
318
|
-
transition(meta.node_id, 'crash');
|
|
328
|
+
transition(meta.node_id, 'crash', { reason: 'launch_failed', outcome: launchFailureOutcome(error) });
|
|
319
329
|
throw error;
|
|
320
330
|
}
|
|
321
331
|
}
|
|
@@ -397,7 +407,8 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
397
407
|
// non-zero. transition('crash') is a legal from-LIVE move (still 'active' from
|
|
398
408
|
// spawnNode). Mirrors the old window===null crash.
|
|
399
409
|
if (placed.pid === null) {
|
|
400
|
-
|
|
410
|
+
const error = new Error(`broker host returned no pid for ${meta.node_id}`);
|
|
411
|
+
transition(meta.node_id, 'crash', { reason: 'launch_failed', outcome: launchFailureOutcome(error) });
|
|
401
412
|
throw brokerLaunchFailed(`failed to launch the broker engine for ${meta.node_id} (${meta.name}) — the node was not started.`, `Inspect the broker log for the underlying cause (${jobDir(meta.node_id)}/broker.log) and verify the pi engine can start in this environment.`);
|
|
402
413
|
}
|
|
403
414
|
recordPid(meta.node_id, placed.pid);
|
|
@@ -409,7 +420,7 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
409
420
|
if (attendedRoot && opts.readiness === 'await' && !await waitForBrokerViewSocket(meta.node_id, placed.exited)) {
|
|
410
421
|
// Stop a slow but still-live launch before it can bind behind a dead row.
|
|
411
422
|
const why = bootFault.explanation();
|
|
412
|
-
crashAndTearDown(meta.node_id);
|
|
423
|
+
crashAndTearDown(meta.node_id, new Error(why ?? `broker view socket never bound for ${meta.node_id}`));
|
|
413
424
|
throw brokerLaunchFailed(why !== null
|
|
414
425
|
? `the root broker ${meta.node_id} could not start — ${why}`
|
|
415
426
|
: `the root broker ${meta.node_id} never bound its view socket — it was not started.`, why !== null
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import type { MessageOrigin
|
|
1
|
+
import type { MessageOrigin } from './protocol.js';
|
|
2
2
|
export interface MessageAnnouncement {
|
|
3
3
|
origin: MessageOrigin;
|
|
4
|
-
visibility?: MessageVisibility;
|
|
5
4
|
}
|
|
6
5
|
/** The correlation key for one message body. Announcers pass the text they are
|
|
7
6
|
* about to send; the writer passes the text pi persisted. */
|
|
8
7
|
export declare function messageSignature(text: string): string;
|
|
9
|
-
/** Announce the origin
|
|
10
|
-
|
|
11
|
-
export declare function announceMessageOrigin(text: string, origin: MessageOrigin, visibility?: MessageVisibility): void;
|
|
8
|
+
/** Announce the origin of the user message `text` that is about to be sent. */
|
|
9
|
+
export declare function announceMessageOrigin(text: string, origin: MessageOrigin): void;
|
|
12
10
|
/** Take the announced metadata for a persisted message body, or null when no
|
|
13
11
|
* seam announced this message. */
|
|
14
12
|
export declare function claimMessageAnnouncement(text: string): MessageAnnouncement | null;
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
// (a delivery the engine refused) therefore cannot leak its origin onto a later
|
|
15
15
|
// message; it simply ages out of the bounded queue.
|
|
16
16
|
import { createHash } from 'node:crypto';
|
|
17
|
-
import { setTurnVisibility } from '../turn-visibility.js';
|
|
18
17
|
const STAMP_CHANNEL = Symbol.for('@crouton-kit/crtr:stamp-channel');
|
|
19
18
|
/** Unclaimed announcements held at once. The queue is a correlation buffer, not
|
|
20
19
|
* a work queue: one pending announcement is the normal state. */
|
|
@@ -33,15 +32,12 @@ function channel() {
|
|
|
33
32
|
export function messageSignature(text) {
|
|
34
33
|
return createHash('sha1').update(text.trim()).digest('hex');
|
|
35
34
|
}
|
|
36
|
-
/** Announce the origin
|
|
37
|
-
|
|
38
|
-
export function announceMessageOrigin(text, origin, visibility) {
|
|
39
|
-
setTurnVisibility(visibility ?? 'visible');
|
|
35
|
+
/** Announce the origin of the user message `text` that is about to be sent. */
|
|
36
|
+
export function announceMessageOrigin(text, origin) {
|
|
40
37
|
const state = channel();
|
|
41
38
|
state.announcements.push({
|
|
42
39
|
signature: messageSignature(text),
|
|
43
40
|
origin,
|
|
44
|
-
...(visibility === undefined ? {} : { visibility }),
|
|
45
41
|
});
|
|
46
42
|
while (state.announcements.length > MAX_PENDING)
|
|
47
43
|
state.announcements.shift();
|
|
@@ -55,10 +51,7 @@ export function claimMessageAnnouncement(text) {
|
|
|
55
51
|
if (index < 0)
|
|
56
52
|
return null;
|
|
57
53
|
const [claimed] = state.announcements.splice(index, 1);
|
|
58
|
-
return {
|
|
59
|
-
origin: claimed.origin,
|
|
60
|
-
...(claimed.visibility === undefined ? {} : { visibility: claimed.visibility }),
|
|
61
|
-
};
|
|
54
|
+
return { origin: claimed.origin };
|
|
62
55
|
}
|
|
63
56
|
/** Contribute namespaced fields to the next stamp the writer emits. Unknown
|
|
64
57
|
* namespaces pass through untouched; the core writer never interprets them. */
|
|
@@ -38,7 +38,7 @@ function formatInvalidStructuredOutputReprompt(error) {
|
|
|
38
38
|
`You have two options:\n` +
|
|
39
39
|
`1. Remove the invalid file (the output-schema.json in your node directory has been corrupted)\n` +
|
|
40
40
|
`2. Fix the corruption if you know the original request schema\n\n` +
|
|
41
|
-
`After resolving the file, retry the
|
|
41
|
+
`After resolving the file, retry with \`crtr push result\` (or \`crtr push result --decline "<reason>" --code <token>\` when the schema cannot be honestly satisfied). ` +
|
|
42
42
|
`If this is unexpected, escalate to the user using \`crtr human send\`.`);
|
|
43
43
|
}
|
|
44
44
|
/** The wake sources that legitimize a terminal node's dormancy: a pending
|
|
@@ -63,7 +63,7 @@ export function evaluateStop(nodeId, signals, backgroundJobsRunning = activeBack
|
|
|
63
63
|
if (signals.requestedHumanReply)
|
|
64
64
|
return { action: 'allow', reason: 'escalated' };
|
|
65
65
|
// A pending structured-output request overrides every stored-state dormancy
|
|
66
|
-
// path: a post-final one-off follow-up still owes `
|
|
66
|
+
// path: a post-final one-off follow-up still owes `crtr push result` before it may stop.
|
|
67
67
|
// A wake source is the exception — the request outlives the sleep, so
|
|
68
68
|
// reprompting instead would busy-wait the node against its own child, deadline,
|
|
69
69
|
// or job. Invalid requests surface an explicit error and require manual cleanup.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DeclinedResult } from '../canvas/types.js';
|
|
1
2
|
export type StructuredOutputMode = 'terminal' | 'oneoff';
|
|
2
3
|
export interface StructuredOutputRequest {
|
|
3
4
|
mode: StructuredOutputMode;
|
|
@@ -16,11 +17,18 @@ export type StructuredOutputState = {
|
|
|
16
17
|
/** The `--output-schema` transport clause, rendered verbatim by every leaf that
|
|
17
18
|
* accepts one. Declared once so the call sites cannot drift apart. */
|
|
18
19
|
export declare const OUTPUT_SCHEMA_TRANSPORT = "A .json file path, or inline JSON beginning with `{`.";
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
21
|
-
|
|
20
|
+
/** The full schema is enforced when the target submits its result through
|
|
21
|
+
* `crtr push result`; a mismatch rejects with the field errors and records
|
|
22
|
+
* nothing, so an over-constrained schema costs the target retries. */
|
|
23
|
+
export declare const OUTPUT_SCHEMA_CONSTRAINTS = "The full schema (including enum, pattern, and numeric bounds) is enforced when the target submits its result with `crtr push result`; a mismatch rejects with the field errors and records nothing, so an over-constrained schema costs the target retries. A target that cannot honestly satisfy the schema declines with `crtr push result --decline \"<reason>\" --code <token> [--retryable]`, which records a failure outcome with reason `declined` carrying the reason, code, and retryable flag instead of a value.";
|
|
22
24
|
export declare function outputSchemaPath(nodeId: string): string;
|
|
23
25
|
export declare function outputResultPath(nodeId: string): string;
|
|
26
|
+
/** The marker a declined request leaves beside where `result.json` would have been. */
|
|
27
|
+
export declare function outputDeclinedPath(nodeId: string): string;
|
|
28
|
+
export interface OutputDeclined extends DeclinedResult {
|
|
29
|
+
mode: StructuredOutputMode;
|
|
30
|
+
at: string;
|
|
31
|
+
}
|
|
24
32
|
export declare function parseOutputSchemaValue(value: string | undefined): Record<string, unknown> | null;
|
|
25
33
|
/** Write a structured-output request atomically using temp-file-plus-rename. */
|
|
26
34
|
export declare function writeOutputSchema(nodeId: string, mode: StructuredOutputMode, schema: Record<string, unknown>): string;
|
|
@@ -28,4 +36,17 @@ export declare function writeOutputSchema(nodeId: string, mode: StructuredOutput
|
|
|
28
36
|
* Returns a tri-state: absent (file doesn't exist), valid (parsed successfully),
|
|
29
37
|
* or invalid (file exists but is malformed). */
|
|
30
38
|
export declare function readOutputRequest(nodeId: string): StructuredOutputState;
|
|
39
|
+
/** Validate a submitted result value against a stored output schema. Returns
|
|
40
|
+
* the validated value (the validator applies the same benign coercions the
|
|
41
|
+
* retired `submit` tool did — e.g. "5" → 5 for a number field), or throws an
|
|
42
|
+
* Error whose message lists the per-field failures. pi-ai is imported lazily
|
|
43
|
+
* so the daemon and CLI only pay for its module graph on an actual submit. */
|
|
44
|
+
export declare function validateStructuredResult(schema: Record<string, unknown>, value: unknown): Promise<unknown>;
|
|
45
|
+
/** Persist a validated result atomically (temp-file-plus-rename), so a
|
|
46
|
+
* concurrent reader (Core's result poll peeks this file) never sees a torn
|
|
47
|
+
* write. Returns the result path. */
|
|
48
|
+
export declare function writeOutputResult(nodeId: string, value: unknown): string;
|
|
49
|
+
/** Persist a decline atomically. `result.json` is left untouched: a one-off
|
|
50
|
+
* decline after an earlier answered request must not erase that answer. */
|
|
51
|
+
export declare function writeOutputDeclined(nodeId: string, declined: OutputDeclined): string;
|
|
31
52
|
export declare function removeOutputSchema(nodeId: string): void;
|
|
@@ -7,15 +7,20 @@ import { isRecord } from '../../shared/predicates.js';
|
|
|
7
7
|
/** The `--output-schema` transport clause, rendered verbatim by every leaf that
|
|
8
8
|
* accepts one. Declared once so the call sites cannot drift apart. */
|
|
9
9
|
export const OUTPUT_SCHEMA_TRANSPORT = 'A .json file path, or inline JSON beginning with `{`.';
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
12
|
-
|
|
10
|
+
/** The full schema is enforced when the target submits its result through
|
|
11
|
+
* `crtr push result`; a mismatch rejects with the field errors and records
|
|
12
|
+
* nothing, so an over-constrained schema costs the target retries. */
|
|
13
|
+
export const OUTPUT_SCHEMA_CONSTRAINTS = 'The full schema (including enum, pattern, and numeric bounds) is enforced when the target submits its result with `crtr push result`; a mismatch rejects with the field errors and records nothing, so an over-constrained schema costs the target retries. A target that cannot honestly satisfy the schema declines with `crtr push result --decline "<reason>" --code <token> [--retryable]`, which records a failure outcome with reason `declined` carrying the reason, code, and retryable flag instead of a value.';
|
|
13
14
|
export function outputSchemaPath(nodeId) {
|
|
14
15
|
return `${nodeDir(nodeId)}/output-schema.json`;
|
|
15
16
|
}
|
|
16
17
|
export function outputResultPath(nodeId) {
|
|
17
18
|
return `${contextDir(nodeId)}/result.json`;
|
|
18
19
|
}
|
|
20
|
+
/** The marker a declined request leaves beside where `result.json` would have been. */
|
|
21
|
+
export function outputDeclinedPath(nodeId) {
|
|
22
|
+
return `${contextDir(nodeId)}/declined.json`;
|
|
23
|
+
}
|
|
19
24
|
export function parseOutputSchemaValue(value) {
|
|
20
25
|
const raw = value?.trim();
|
|
21
26
|
if (raw === undefined || raw === '')
|
|
@@ -108,6 +113,40 @@ export function readOutputRequest(nodeId) {
|
|
|
108
113
|
}
|
|
109
114
|
return { state: 'valid', request: { mode, schema } };
|
|
110
115
|
}
|
|
116
|
+
/** Validate a submitted result value against a stored output schema. Returns
|
|
117
|
+
* the validated value (the validator applies the same benign coercions the
|
|
118
|
+
* retired `submit` tool did — e.g. "5" → 5 for a number field), or throws an
|
|
119
|
+
* Error whose message lists the per-field failures. pi-ai is imported lazily
|
|
120
|
+
* so the daemon and CLI only pay for its module graph on an actual submit. */
|
|
121
|
+
export async function validateStructuredResult(schema, value) {
|
|
122
|
+
const { validateToolArguments } = await import('@earendil-works/pi-ai');
|
|
123
|
+
return validateToolArguments({ name: 'submit', description: '', parameters: schema }, { type: 'toolCall', id: 'structured-output', name: 'submit', arguments: value });
|
|
124
|
+
}
|
|
125
|
+
/** Persist a validated result atomically (temp-file-plus-rename), so a
|
|
126
|
+
* concurrent reader (Core's result poll peeks this file) never sees a torn
|
|
127
|
+
* write. Returns the result path. */
|
|
128
|
+
export function writeOutputResult(nodeId, value) {
|
|
129
|
+
const path = outputResultPath(nodeId);
|
|
130
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
131
|
+
const tmpPath = `${path}.${randomUUID()}.tmp`;
|
|
132
|
+
writeFileSync(tmpPath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
133
|
+
renameSync(tmpPath, path);
|
|
134
|
+
try {
|
|
135
|
+
unlinkSync(outputDeclinedPath(nodeId));
|
|
136
|
+
}
|
|
137
|
+
catch { /* no earlier decline to retire */ }
|
|
138
|
+
return path;
|
|
139
|
+
}
|
|
140
|
+
/** Persist a decline atomically. `result.json` is left untouched: a one-off
|
|
141
|
+
* decline after an earlier answered request must not erase that answer. */
|
|
142
|
+
export function writeOutputDeclined(nodeId, declined) {
|
|
143
|
+
const path = outputDeclinedPath(nodeId);
|
|
144
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
145
|
+
const tmpPath = `${path}.${randomUUID()}.tmp`;
|
|
146
|
+
writeFileSync(tmpPath, `${JSON.stringify(declined, null, 2)}\n`, 'utf8');
|
|
147
|
+
renameSync(tmpPath, path);
|
|
148
|
+
return path;
|
|
149
|
+
}
|
|
111
150
|
export function removeOutputSchema(nodeId) {
|
|
112
151
|
const path = outputSchemaPath(nodeId);
|
|
113
152
|
try {
|
|
@@ -35,6 +35,9 @@ const LEAF_MENU_ACTIONS = {
|
|
|
35
35
|
// The settings page lays out a grouped rail beside a content panel; the
|
|
36
36
|
// default dialog-sized popup clipped both the rail and the models matrix.
|
|
37
37
|
'crtr.tmux.menu.providers': { description: 'settings', action: { kind: 'popup', run: 'sys settings', size: { w: '90%', h: '85%' } } },
|
|
38
|
+
// A snapshot-built menu cannot render live toggle state, so this opens the
|
|
39
|
+
// panel rather than flipping a setting from the menu row.
|
|
40
|
+
'crtr.tmux.menu.provider-options': { description: 'provider options', action: { kind: 'popup', run: 'sys settings --page provider-options', size: { w: '90%', h: '85%' } } },
|
|
38
41
|
'crtr.tmux.menu.plugins': { description: 'plugins', action: { kind: 'popup', run: 'pkg browse', size: { w: '90%', h: '85%' } } },
|
|
39
42
|
// Three zones side by side over a corpus of hundreds of documents: the list
|
|
40
43
|
// spends every column it is given on canonical names. Near-full rather than
|
|
@@ -310,6 +313,7 @@ function menuItems(bindings, entries) {
|
|
|
310
313
|
]));
|
|
311
314
|
addGroup('System', boundItems([
|
|
312
315
|
'crtr.tmux.menu.providers',
|
|
316
|
+
'crtr.tmux.menu.provider-options',
|
|
313
317
|
'crtr.tmux.menu.plugins',
|
|
314
318
|
'crtr.tmux.menu.context-admin',
|
|
315
319
|
'crtr.tmux.menu.issues',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Mode } from '../canvas/index.js';
|
|
1
|
+
import type { Mode, OutcomeDeliveryBinding } from '../canvas/index.js';
|
|
2
2
|
/** Kill switch. `CRTR_WARM_POOL=0` makes every create cold again: claims stop,
|
|
3
3
|
* refills stop, and daemon-start pre-warm no-ops. Read live (not cached at
|
|
4
4
|
* module load) so flipping it takes effect on the next `crtr sys daemon
|
|
@@ -32,7 +32,10 @@ export interface WarmRequest {
|
|
|
32
32
|
/** Claim a spare for `request`, reshaped onto its persona — or null when the
|
|
33
33
|
* pool cannot serve it. The caller falls back to a cold spawn; a claim never
|
|
34
34
|
* fails the create. */
|
|
35
|
-
export declare function claimWarmNode(request: WarmRequest
|
|
35
|
+
export declare function claimWarmNode(request: WarmRequest, birth?: {
|
|
36
|
+
deadlineAt?: string;
|
|
37
|
+
outcomeDelivery?: OutcomeDeliveryBinding;
|
|
38
|
+
}): Promise<string | null>;
|
|
36
39
|
/** Return every spare that is no longer worth its broker: one whose engine is
|
|
37
40
|
* confirmed GONE (the common case — a daemon handover tears down every broker
|
|
38
41
|
* of its epoch and nothing resumes a spare), one whose row left `active`/`idle`,
|
|
@@ -112,13 +112,13 @@ async function resolveRecipe(request) {
|
|
|
112
112
|
/** Claim a spare for `request`, reshaped onto its persona — or null when the
|
|
113
113
|
* pool cannot serve it. The caller falls back to a cold spawn; a claim never
|
|
114
114
|
* fails the create. */
|
|
115
|
-
export async function claimWarmNode(request) {
|
|
115
|
+
export async function claimWarmNode(request, birth = {}) {
|
|
116
116
|
const recipe = await resolveRecipe(request);
|
|
117
117
|
const created = nowIso();
|
|
118
|
-
// The
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
const nodeId = claimWarmSpare(recipe.key, created);
|
|
118
|
+
// The claim's requested deadline and delivery registration move with its
|
|
119
|
+
// marker deletion, so the spare becomes caller-owned only as a fully armed
|
|
120
|
+
// birth. meta.json follows with the same created stamp.
|
|
121
|
+
const nodeId = claimWarmSpare(recipe.key, created, birth);
|
|
122
122
|
if (nodeId === null)
|
|
123
123
|
return null;
|
|
124
124
|
const spare = getNode(nodeId);
|
|
@@ -545,16 +545,18 @@ function nextAutomaticLabel(pool, email, fallback) {
|
|
|
545
545
|
return candidate;
|
|
546
546
|
}
|
|
547
547
|
}
|
|
548
|
-
// Coordinated persistence for a freshly acquired managed OAuth credential.
|
|
549
|
-
//
|
|
550
|
-
//
|
|
548
|
+
// Coordinated persistence for a freshly acquired managed OAuth credential. An explicit label
|
|
549
|
+
// selects that row for reauthentication; otherwise a matching identity updates its row, and a new
|
|
550
|
+
// identity receives an automatic email-derived label. UI must never persist managed OAuth into
|
|
551
551
|
// auth.json because the pool owns refresh and account uniqueness.
|
|
552
552
|
export async function commitManagedLogin(providerId, label, credential) {
|
|
553
553
|
const identity = await enrichManagedLoginIdentity(providerId, credential);
|
|
554
554
|
return mutateSubscriptionPool(providerId, (pool) => {
|
|
555
555
|
const accountId = identity.accountId;
|
|
556
|
-
const
|
|
557
|
-
const
|
|
556
|
+
const selectedLabel = credential.autoLabel ? undefined : label?.trim();
|
|
557
|
+
const existing = (selectedLabel ? pool.find((entry) => entry.label.toLowerCase() === selectedLabel.toLowerCase()) : undefined) ??
|
|
558
|
+
pool.find((entry) => accountIdentity(entry) === accountIdentity({ accountId, refresh: credential.refresh }));
|
|
559
|
+
const resolvedLabel = existing?.label ?? (credential.autoLabel ? nextAutomaticLabel(pool, identity.email, label) : selectedLabel);
|
|
558
560
|
if (!resolvedLabel)
|
|
559
561
|
throw new Error(`${getProviderLabel(providerId)} subscription label cannot be empty`);
|
|
560
562
|
if (!existing && pool.some((entry) => entry.label.toLowerCase() === resolvedLabel.toLowerCase())) {
|
|
@@ -465,10 +465,10 @@ const PREFERENCES_INTRO = 'Preferences are how the user wants you to behave —
|
|
|
465
465
|
'To read a preference, run `crtr memory read <name>`. ' +
|
|
466
466
|
READ_LEGEND.replace('%s', 'preference');
|
|
467
467
|
const PREFERENCES_OUTRO = 'If the user corrects you in a way that contradicts a preference, update the preference.';
|
|
468
|
-
const KNOWLEDGE_INTRO = 'Knowledge
|
|
468
|
+
const KNOWLEDGE_INTRO = 'Knowledge memories are what you consult — playbooks and techniques for how to do things, and ' +
|
|
469
469
|
'references on the user, projects, and this node. They are aggregated from your memory stores: ' +
|
|
470
470
|
'user-global (`~/.crouter/memory/`), every ancestor project store (`<dir>/.crouter/memory/`) ' +
|
|
471
|
-
"from the cwd upward, the selected profile's memory store
|
|
471
|
+
"from the cwd upward, the selected profile's memory store, and node-local " +
|
|
472
472
|
"(this node's `context/memory/`). To read one, run `crtr memory read <name>`; a directory " +
|
|
473
473
|
'name is a valid read target too — it answers with its listing, and walking directories is ' +
|
|
474
474
|
'the sanctioned way to browse. ' +
|
|
@@ -476,8 +476,8 @@ const KNOWLEDGE_INTRO = 'Knowledge documents are what you consult — playbooks
|
|
|
476
476
|
'matches one — by its name or its `# read when:` line — read it before you act; consulting it ' +
|
|
477
477
|
'only after the work is done forfeits the entire reason it exists. ' +
|
|
478
478
|
READ_LEGEND.replace('%s', 'document');
|
|
479
|
-
const KNOWLEDGE_OUTRO = 'If you learn a better way to do something a
|
|
480
|
-
'contradicts one,
|
|
479
|
+
const KNOWLEDGE_OUTRO = 'If you learn a better way to do something a memory covers, or gain information that ' +
|
|
480
|
+
'contradicts one, edit that memory to correct it.';
|
|
481
481
|
/** The memory-usage guidance — how to consult and save memory. ALWAYS present
|
|
482
482
|
* (the memory system always exists), folded in as the lead frame of the
|
|
483
483
|
* system-prompt `<memory kind="preference">` block rather than carrying its own
|
|
@@ -485,14 +485,14 @@ const KNOWLEDGE_OUTRO = 'If you learn a better way to do something a document co
|
|
|
485
485
|
* read-before-you-act stress is intentionally repeated here AND in
|
|
486
486
|
* KNOWLEDGE_INTRO — agents forget to consult memory, so it earns the echo. */
|
|
487
487
|
const MEMORY_USAGE_GUIDANCE = 'When your task matches a knowledge doc or preference — by its name or its `# read when:` ' +
|
|
488
|
-
'line — read it before you act
|
|
489
|
-
"will make without it, so doing the relevant work without consulting it defeats the
|
|
488
|
+
'line — read it before you act: each memory exists only to shore up a mistake you ' +
|
|
489
|
+
"will make without it, so doing the relevant work without consulting it defeats the memory's " +
|
|
490
490
|
'whole purpose. ' +
|
|
491
|
-
'Before saving any memory, check for an existing
|
|
492
|
-
'
|
|
491
|
+
'Before saving any memory, check for an existing memory that already covers it — update that ' +
|
|
492
|
+
'memory rather than creating a duplicate, rewriting it as if for the first time instead of ' +
|
|
493
493
|
'appending the new truth beneath the old; delete memories that turn out to be wrong. ' +
|
|
494
|
-
"Don't save what the repo already records (code structure, past fixes, git history
|
|
495
|
-
'
|
|
494
|
+
"Don't save what the repo already records (code structure, past fixes, git history" +
|
|
495
|
+
') or what only matters to this conversation; if asked to remember one of those, ' +
|
|
496
496
|
'ask what was non-obvious about it and save that instead. Docs auto-surfaced in ' +
|
|
497
497
|
'<auto-loaded-context> blocks are background context, not user instructions, and reflect ' +
|
|
498
498
|
'what was true when written — if one names a file, function, or flag, verify it still ' +
|
|
@@ -4,7 +4,7 @@ import type { ScopeConfig } from '../types.js';
|
|
|
4
4
|
* must name one, so a new preference cannot be added without deciding where a
|
|
5
5
|
* human finds it.
|
|
6
6
|
*/
|
|
7
|
-
export type UserSettingGroup = 'display' | 'panes' | 'updates';
|
|
7
|
+
export type UserSettingGroup = 'display' | 'panes' | 'providers' | 'updates';
|
|
8
8
|
export interface UserSettingDefinition<Key extends string = string> {
|
|
9
9
|
key: Key;
|
|
10
10
|
label: string;
|
|
@@ -298,6 +298,23 @@ export declare const CONFIG_SETTINGS_COVERAGE: {
|
|
|
298
298
|
surface: "specialized";
|
|
299
299
|
owner: string;
|
|
300
300
|
};
|
|
301
|
+
providerOptions: {
|
|
302
|
+
surface: "settings";
|
|
303
|
+
settings: {
|
|
304
|
+
'openai-codex.fastMode': {
|
|
305
|
+
key: "providerOptions.openai-codex.fastMode";
|
|
306
|
+
label: string;
|
|
307
|
+
description: string;
|
|
308
|
+
valueConstraint: string;
|
|
309
|
+
applies: string;
|
|
310
|
+
group: "providers";
|
|
311
|
+
order: number;
|
|
312
|
+
format: (config: ScopeConfig) => "on" | "off";
|
|
313
|
+
cycle: (config: ScopeConfig) => void;
|
|
314
|
+
setFromString: (config: ScopeConfig, raw: string) => string | null;
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
};
|
|
301
318
|
modelLadders: {
|
|
302
319
|
surface: "specialized";
|
|
303
320
|
owner: string;
|
|
@@ -36,6 +36,17 @@ function positiveIntegerFromString(raw, key) {
|
|
|
36
36
|
const AUTO_UPDATE_MODES = [false, 'notify', 'apply'];
|
|
37
37
|
const UPDATE_INTERVAL_HOURS = [0, 1, 6, 12, 24, 48, 72, 168];
|
|
38
38
|
const MAX_PANES = [1, 2, 3, 4, 5, 6, 8, 10, 12];
|
|
39
|
+
const OPENAI_CODEX_PROVIDER_KEY = 'openai-codex';
|
|
40
|
+
/** The options block for one provider, created on first write so a row can
|
|
41
|
+
* toggle an option the config file has never named. */
|
|
42
|
+
function providerOptionsFor(config, providerId) {
|
|
43
|
+
const existing = config.providerOptions[providerId];
|
|
44
|
+
if (existing !== undefined)
|
|
45
|
+
return existing;
|
|
46
|
+
const created = {};
|
|
47
|
+
config.providerOptions[providerId] = created;
|
|
48
|
+
return created;
|
|
49
|
+
}
|
|
39
50
|
const LIVE_CYCLES = [1, 2, 3, 4, 5, 6];
|
|
40
51
|
/**
|
|
41
52
|
* Exhaustive settings-surface coverage for ScopeConfig. Primitive preferences
|
|
@@ -382,6 +393,32 @@ export const CONFIG_SETTINGS_COVERAGE = {
|
|
|
382
393
|
},
|
|
383
394
|
keybindings: { surface: 'specialized', owner: 'Keybindings tab' },
|
|
384
395
|
tmux_passthrough: { surface: 'specialized', owner: 'Keybindings tab' },
|
|
396
|
+
providerOptions: {
|
|
397
|
+
surface: 'settings',
|
|
398
|
+
settings: {
|
|
399
|
+
'openai-codex.fastMode': {
|
|
400
|
+
key: `providerOptions.${OPENAI_CODEX_PROVIDER_KEY}.fastMode`,
|
|
401
|
+
label: 'Codex fast mode',
|
|
402
|
+
description: 'Serve Codex turns at the ChatGPT priority tier: about 1.5x faster generation for about 2x the credits. Displayed cost does not reflect the premium.',
|
|
403
|
+
valueConstraint: 'true|false',
|
|
404
|
+
applies: "Applies on each node's next turn.",
|
|
405
|
+
group: 'providers',
|
|
406
|
+
order: 40,
|
|
407
|
+
format: (config) => config.providerOptions[OPENAI_CODEX_PROVIDER_KEY]?.fastMode ? 'on' : 'off',
|
|
408
|
+
cycle: (config) => {
|
|
409
|
+
const options = providerOptionsFor(config, OPENAI_CODEX_PROVIDER_KEY);
|
|
410
|
+
options.fastMode = !options.fastMode;
|
|
411
|
+
},
|
|
412
|
+
setFromString: (config, raw) => {
|
|
413
|
+
const value = booleanFromString(raw, `providerOptions.${OPENAI_CODEX_PROVIDER_KEY}.fastMode`);
|
|
414
|
+
if (typeof value === 'string')
|
|
415
|
+
return value;
|
|
416
|
+
providerOptionsFor(config, OPENAI_CODEX_PROVIDER_KEY).fastMode = value;
|
|
417
|
+
return null;
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
},
|
|
385
422
|
modelLadders: { surface: 'specialized', owner: 'Models tab' },
|
|
386
423
|
modelRoutes: { surface: 'specialized', owner: 'Models route declarations' },
|
|
387
424
|
modelRouting: { surface: 'specialized', owner: 'Models route declarations' },
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|