@namzu/cli 20.0.0 → 21.0.0
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/CHANGELOG.md +255 -0
- package/README.md +42 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +24 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/acp.js +6 -1
- package/dist/commands/acp.js.map +1 -1
- package/dist/commands/run-stream.d.ts.map +1 -1
- package/dist/commands/run-stream.js +9 -1
- package/dist/commands/run-stream.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +14 -11
- package/dist/commands/run.js.map +1 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +21 -0
- package/dist/config/load.js.map +1 -1
- package/dist/config/schema.d.ts +11 -4
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/doctor/checks/chain.d.ts.map +1 -1
- package/dist/doctor/checks/chain.js +11 -5
- package/dist/doctor/checks/chain.js.map +1 -1
- package/dist/doctor/checks/credentials.d.ts.map +1 -1
- package/dist/doctor/checks/credentials.js +5 -1
- package/dist/doctor/checks/credentials.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/providers/access.d.ts +7 -0
- package/dist/integrations/providers/access.d.ts.map +1 -0
- package/dist/integrations/providers/access.js +17 -0
- package/dist/integrations/providers/access.js.map +1 -0
- package/dist/integrations/providers/discover.d.ts +17 -54
- package/dist/integrations/providers/discover.d.ts.map +1 -1
- package/dist/integrations/providers/discover.js +46 -58
- package/dist/integrations/providers/discover.js.map +1 -1
- package/dist/integrations/providers/harness-credentials.d.ts +14 -0
- package/dist/integrations/providers/harness-credentials.d.ts.map +1 -1
- package/dist/integrations/providers/harness-credentials.js +46 -0
- package/dist/integrations/providers/harness-credentials.js.map +1 -1
- package/dist/integrations/providers/model-reasoning.d.ts +4 -0
- package/dist/integrations/providers/model-reasoning.d.ts.map +1 -0
- package/dist/integrations/providers/model-reasoning.js +46 -0
- package/dist/integrations/providers/model-reasoning.js.map +1 -0
- package/dist/integrations/providers/register.d.ts +1 -1
- package/dist/integrations/providers/register.d.ts.map +1 -1
- package/dist/integrations/providers/register.js +11 -1
- package/dist/integrations/providers/register.js.map +1 -1
- package/dist/integrations/providers/registry.d.ts +4 -4
- package/dist/integrations/providers/registry.d.ts.map +1 -1
- package/dist/integrations/providers/registry.js +23 -0
- package/dist/integrations/providers/registry.js.map +1 -1
- package/dist/integrations/sessions/context-inventory.d.ts +4 -0
- package/dist/integrations/sessions/context-inventory.d.ts.map +1 -0
- package/dist/integrations/sessions/context-inventory.js +44 -0
- package/dist/integrations/sessions/context-inventory.js.map +1 -0
- package/dist/integrations/sessions/conversation-search.d.ts +60 -0
- package/dist/integrations/sessions/conversation-search.d.ts.map +1 -0
- package/dist/integrations/sessions/conversation-search.js +461 -0
- package/dist/integrations/sessions/conversation-search.js.map +1 -0
- package/dist/integrations/sessions/task-context.d.ts +4 -0
- package/dist/integrations/sessions/task-context.d.ts.map +1 -0
- package/dist/integrations/sessions/task-context.js +73 -0
- package/dist/integrations/sessions/task-context.js.map +1 -0
- package/dist/integrations/subagents/activity.d.ts +3 -1
- package/dist/integrations/subagents/activity.d.ts.map +1 -1
- package/dist/integrations/subagents/activity.js +20 -2
- package/dist/integrations/subagents/activity.js.map +1 -1
- package/dist/integrations/subagents/runtime.d.ts +7 -4
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/subagents/runtime.js +105 -17
- package/dist/integrations/subagents/runtime.js.map +1 -1
- package/dist/memory/io.d.ts +12 -0
- package/dist/memory/io.d.ts.map +1 -0
- package/dist/memory/io.js +177 -0
- package/dist/memory/io.js.map +1 -0
- package/dist/memory/presentation.d.ts +4 -1
- package/dist/memory/presentation.d.ts.map +1 -1
- package/dist/memory/presentation.js +16 -2
- package/dist/memory/presentation.js.map +1 -1
- package/dist/memory/store.d.ts +17 -3
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js +61 -35
- package/dist/memory/store.js.map +1 -1
- package/dist/output-schema.d.ts +4 -0
- package/dist/output-schema.d.ts.map +1 -0
- package/dist/output-schema.js +18 -0
- package/dist/output-schema.js.map +1 -0
- package/dist/tui/AgentExplorer.d.ts +13 -2
- package/dist/tui/AgentExplorer.d.ts.map +1 -1
- package/dist/tui/AgentExplorer.js +85 -23
- package/dist/tui/AgentExplorer.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +454 -64
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/BrandHeader.d.ts +1 -4
- package/dist/tui/BrandHeader.d.ts.map +1 -1
- package/dist/tui/BrandHeader.js +2 -6
- package/dist/tui/BrandHeader.js.map +1 -1
- package/dist/tui/Composer.d.ts +2 -1
- package/dist/tui/Composer.d.ts.map +1 -1
- package/dist/tui/Composer.js +9 -2
- package/dist/tui/Composer.js.map +1 -1
- package/dist/tui/PermissionOverlay.d.ts +1 -1
- package/dist/tui/PermissionOverlay.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.js +8 -1
- package/dist/tui/PermissionOverlay.js.map +1 -1
- package/dist/tui/Picker.d.ts.map +1 -1
- package/dist/tui/Picker.js +94 -21
- package/dist/tui/Picker.js.map +1 -1
- package/dist/tui/TextPrompt.d.ts +3 -1
- package/dist/tui/TextPrompt.d.ts.map +1 -1
- package/dist/tui/TextPrompt.js +55 -9
- package/dist/tui/TextPrompt.js.map +1 -1
- package/dist/tui/Transcript.d.ts.map +1 -1
- package/dist/tui/Transcript.js +1 -1
- package/dist/tui/Transcript.js.map +1 -1
- package/dist/tui/agent.d.ts +18 -3
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +174 -58
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/model-choices.d.ts +3 -1
- package/dist/tui/model-choices.d.ts.map +1 -1
- package/dist/tui/model-choices.js +12 -1
- package/dist/tui/model-choices.js.map +1 -1
- package/dist/tui/model-search.d.ts +4 -0
- package/dist/tui/model-search.d.ts.map +1 -0
- package/dist/tui/model-search.js +11 -0
- package/dist/tui/model-search.js.map +1 -0
- package/dist/tui/model-selection-intent.d.ts +8 -0
- package/dist/tui/model-selection-intent.d.ts.map +1 -0
- package/dist/tui/model-selection-intent.js +96 -0
- package/dist/tui/model-selection-intent.js.map +1 -0
- package/dist/tui/model-switch-tool.d.ts +7 -0
- package/dist/tui/model-switch-tool.d.ts.map +1 -0
- package/dist/tui/model-switch-tool.js +74 -0
- package/dist/tui/model-switch-tool.js.map +1 -0
- package/dist/tui/model-switch.d.ts +35 -0
- package/dist/tui/model-switch.d.ts.map +1 -0
- package/dist/tui/model-switch.js +106 -0
- package/dist/tui/model-switch.js.map +1 -0
- package/dist/tui/permission-review.d.ts.map +1 -1
- package/dist/tui/permission-review.js +25 -12
- package/dist/tui/permission-review.js.map +1 -1
- package/dist/tui/run-interruption.d.ts +7 -0
- package/dist/tui/run-interruption.d.ts.map +1 -1
- package/dist/tui/run-interruption.js +41 -0
- package/dist/tui/run-interruption.js.map +1 -1
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +2 -1
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/types.d.ts +3 -1
- package/dist/tui/types.d.ts.map +1 -1
- package/package.json +8 -7
package/dist/tui/agent.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* `emptySession()` whose `send()` yields a single error event so the UI
|
|
21
21
|
* renders an actionable hint rather than crashing.
|
|
22
22
|
*/
|
|
23
|
-
import { BOOT_EVENT_NAMES, BackgroundJobRegistry, DefaultPathBuilder, DiskCheckpointStore, DiskMemoryStore, DiskTaskStore, EVENT_NAME_ATTRIBUTE, GuardedFetchProvider, PromptContributionRegistry, ProviderRegistry, SESSION_GOAL_TOOL_NAMES,
|
|
23
|
+
import { BOOT_EVENT_NAMES, BackgroundJobRegistry, DefaultPathBuilder, DiskCheckpointStore, DiskMemoryStore, DiskTaskStore, EVENT_NAME_ATTRIBUTE, GuardedFetchProvider, PromptContributionRegistry, ProviderRegistry, SESSION_GOAL_TOOL_NAMES, ToolRegistry, WebFetchTool, batchNeedsReview, buildAskUserQuestionTool, buildMemoryTools, buildSessionGoalTools, compactNow, createComputerUseTool, createMemoryPromoter, createMemoryRecallStep, createReviewHandler, createToolPresenter, generateProjectId, generateRunId, generateSessionId, generateTenantId, generateTopicId, getBuiltinTools, isReviewExempt, query, resumeRun, webGuidanceContribution, withProviderFallback, } from '@namzu/sdk';
|
|
24
24
|
import { SubprocessComputerUseHost } from '@namzu/computer-use';
|
|
25
25
|
import { realpath, stat } from 'node:fs/promises';
|
|
26
26
|
import { join, parse, resolve } from 'node:path';
|
|
@@ -34,15 +34,22 @@ import { resolveSandbox, sandboxResolvedSeverity, } from '../context/sandbox.js'
|
|
|
34
34
|
import { composeTurnSnapshot, readTurnSnapshot } from '../context/turn-snapshot.js';
|
|
35
35
|
import { connectMcpServers, } from '../integrations/mcp/servers.js';
|
|
36
36
|
import { createCliPluginRuntime } from '../integrations/plugins/runtime.js';
|
|
37
|
+
import { hasApiCredential, requiresCredentialForModel } from '../integrations/providers/access.js';
|
|
37
38
|
import { CredentialRefreshRejectedError, CredentialWithdrawnError, PROVIDER_REGISTRY, chainCapabilityDisagreements, chainPositionName, describeAcceptedMismatch, describeCapabilityRefusal, discoverProviders, ensureFreshAnthropicToken, ensureFreshStoredCodexCredential, ensureRegistered, findDetected, isAnthropicOAuthToken, isRegistered, missingCredentialMessage, primaryProvider, readCodexCredentialFile, readPreferences, readSubscriptionCredential, resolveChainCapabilities, sameOAuthCredential, unresolvedMembers, unsupportedProviderMessage, } from '../integrations/providers/index.js';
|
|
39
|
+
import { modelReasoningView } from '../integrations/providers/model-reasoning.js';
|
|
40
|
+
import { createContextInventoryStep } from '../integrations/sessions/context-inventory.js';
|
|
41
|
+
import { buildConversationReadTool, buildConversationSearchTool, } from '../integrations/sessions/conversation-search.js';
|
|
42
|
+
import { createTaskContextStep } from '../integrations/sessions/task-context.js';
|
|
38
43
|
import { ensurePrivateStateDirectory } from '../integrations/state/private-directory.js';
|
|
39
44
|
import { discoverAgentDefinitions } from '../integrations/subagents/definitions.js';
|
|
40
45
|
import { SubagentPathBuilder, resolveSubagentParent } from '../integrations/subagents/parent.js';
|
|
41
46
|
import { CLI_INTERACTIVE_RUN_TIMEOUT_MS } from '../integrations/subagents/policy.js';
|
|
42
47
|
import { createSubagentRuntime } from '../integrations/subagents/runtime.js';
|
|
43
48
|
import { cliLogger } from '../logging.js';
|
|
49
|
+
import { formatMemoryDiagnostics } from '../memory/presentation.js';
|
|
44
50
|
import { composeMemoryPrompt, readMemory } from '../memory/store.js';
|
|
45
51
|
import { projectRunConversation } from './conversation-history.js';
|
|
52
|
+
import { buildSwitchModelTool } from './model-switch-tool.js';
|
|
46
53
|
/**
|
|
47
54
|
* Let one caller stop waiting without cutting a shared queue in the middle.
|
|
48
55
|
*
|
|
@@ -303,10 +310,12 @@ function credentialGap(prefs, detected) {
|
|
|
303
310
|
// one ever did, it is not a credential problem and must not be reported as
|
|
304
311
|
// one — a wrong diagnosis sends the operator to paste a key that would not
|
|
305
312
|
// have helped.
|
|
306
|
-
if (!entry ||
|
|
313
|
+
if (!entry ||
|
|
314
|
+
!entry.constructible ||
|
|
315
|
+
!requiresCredentialForModel(entry, primary.model ?? entry.defaultModel))
|
|
307
316
|
return null;
|
|
308
317
|
const det = findDetected(detected, primary.id);
|
|
309
|
-
if (det?.apiKey)
|
|
318
|
+
if (hasApiCredential(entry, det?.apiKey))
|
|
310
319
|
return null;
|
|
311
320
|
return { providerId: primary.id, reason: missingCredentialMessage(entry) };
|
|
312
321
|
}
|
|
@@ -363,9 +372,12 @@ function foregroundOnlyBash(tool) {
|
|
|
363
372
|
};
|
|
364
373
|
}
|
|
365
374
|
function builtinTools(backgroundJobs) {
|
|
366
|
-
return getBuiltinTools().flatMap((
|
|
367
|
-
if (EXCLUDED_BUILTINS.has(
|
|
375
|
+
return getBuiltinTools().flatMap((source) => {
|
|
376
|
+
if (EXCLUDED_BUILTINS.has(source.name))
|
|
368
377
|
return [];
|
|
378
|
+
const tool = ['bash', 'write', 'edit'].includes(source.name)
|
|
379
|
+
? { ...source, executionBarrier: true }
|
|
380
|
+
: source;
|
|
369
381
|
if (backgroundJobs)
|
|
370
382
|
return [tool];
|
|
371
383
|
if (tool.name === 'job')
|
|
@@ -399,14 +411,8 @@ function buildToolRegistry(cwd, projectStateRoot = join(cwd, '.namzu'), backgrou
|
|
|
399
411
|
// handing `getIndex()` to the synchronous overload before the first store
|
|
400
412
|
// read makes a new process report every persisted memory as absent.
|
|
401
413
|
registry.register(buildMemoryTools(memoryStore));
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
// callers: the session below passes a `taskStore`, so query() registers the
|
|
405
|
-
// task tools deferred and the search has a roster; a sub-agent is built with
|
|
406
|
-
// no task store, so its registry has nothing deferred and the tool could
|
|
407
|
-
// only ever answer "no deferred tools matching X" — a capability advertised
|
|
408
|
-
// every turn that costs a turn to discover is unusable. Mounting it is the
|
|
409
|
-
// caller's decision, made where the roster is known.
|
|
414
|
+
// query() mounts search_tools only if a deferred roster actually exists,
|
|
415
|
+
// after runtime tools are registered. Ordinary CLI task tools are active.
|
|
410
416
|
return { registry, memoryStore };
|
|
411
417
|
}
|
|
412
418
|
/** Refresh plugin skill metadata before each provider operation. */
|
|
@@ -464,7 +470,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
464
470
|
return emptySession(`Unknown provider "${primary.id}" — pick another.`, 'invocation');
|
|
465
471
|
}
|
|
466
472
|
const det = findDetected(detected, primary.id);
|
|
467
|
-
if (entry.
|
|
473
|
+
if (requiresCredentialForModel(entry, primary.model ?? entry.defaultModel) &&
|
|
474
|
+
!hasApiCredential(entry, det?.apiKey)) {
|
|
468
475
|
// The BACKSTOP, not the operator-facing answer. The TUI never reaches this
|
|
469
476
|
// line any more: `probeAgentSession` reports the same gap as a
|
|
470
477
|
// `credentialGap` and the App routes into the picker, where a credential
|
|
@@ -473,7 +480,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
473
480
|
// pieces of advice below are real: an environment variable, or
|
|
474
481
|
// `--provider`. Keeping the refusal is what makes those runs fail rather
|
|
475
482
|
// than quietly start on something else.
|
|
476
|
-
return emptySession(`No credential found for ${entry.label}. Set one of: ${entry.envVars.join(', ')} — or pass --provider with one that is configured.`);
|
|
483
|
+
return emptySession(`No credential found for ${entry.label}${entry.id === 'zen' ? ' with the selected model. Choose muse-spark-1.3-contributor-free for public access' : ''}. Set one of: ${entry.envVars.join(', ')} — or pass --provider with one that is configured.`);
|
|
477
484
|
}
|
|
478
485
|
try {
|
|
479
486
|
await ensureRegistered(primary.id);
|
|
@@ -523,7 +530,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
523
530
|
}
|
|
524
531
|
let provider;
|
|
525
532
|
try {
|
|
526
|
-
provider = constructProvider(primary.id, det, model
|
|
533
|
+
provider = constructProvider(primary.id, det, model, {
|
|
534
|
+
sessionId: scope.sessionId,
|
|
535
|
+
});
|
|
527
536
|
}
|
|
528
537
|
catch (err) {
|
|
529
538
|
return emptySession(`Failed to construct ${entry.label}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -597,7 +606,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
597
606
|
if (fresh === currentToken)
|
|
598
607
|
return;
|
|
599
608
|
try {
|
|
600
|
-
const refreshedProvider = constructProvider('anthropic', { ...det, apiKey: fresh }, model);
|
|
609
|
+
const refreshedProvider = constructProvider('anthropic', { ...det, apiKey: fresh }, model, { sessionId: scope.sessionId });
|
|
601
610
|
// Publish the pair together. If construction fails, both old values
|
|
602
611
|
// remain live and the next operation can retry against the stored token.
|
|
603
612
|
provider = refreshedProvider;
|
|
@@ -634,7 +643,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
634
643
|
expiresAt: credential.expiresAt,
|
|
635
644
|
origin: 'codex-file',
|
|
636
645
|
},
|
|
637
|
-
}, model);
|
|
646
|
+
}, model, { sessionId: scope.sessionId });
|
|
638
647
|
provider = refreshedProvider;
|
|
639
648
|
currentToken = credential.accessToken;
|
|
640
649
|
currentCodexAccount = credential.accountId;
|
|
@@ -652,7 +661,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
652
661
|
}
|
|
653
662
|
if (credential.accessToken === currentToken)
|
|
654
663
|
return;
|
|
655
|
-
provider = constructProvider('anthropic', { ...det, apiKey: credential.accessToken }, model);
|
|
664
|
+
provider = constructProvider('anthropic', { ...det, apiKey: credential.accessToken }, model, { sessionId: scope.sessionId });
|
|
656
665
|
currentToken = credential.accessToken;
|
|
657
666
|
};
|
|
658
667
|
const refreshStoredCodex = async (signal) => {
|
|
@@ -668,7 +677,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
668
677
|
expiresAt: credential.expiresAt,
|
|
669
678
|
origin: 'stored',
|
|
670
679
|
},
|
|
671
|
-
}, model);
|
|
680
|
+
}, model, { sessionId: scope.sessionId });
|
|
672
681
|
provider = refreshedProvider;
|
|
673
682
|
currentToken = credential.accessToken;
|
|
674
683
|
currentCodexAccount = credential.accountId;
|
|
@@ -695,6 +704,12 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
695
704
|
credentialTail = queued.catch(() => { });
|
|
696
705
|
return signal ? observeWithSignal(queued, signal) : queued;
|
|
697
706
|
};
|
|
707
|
+
// The TUI can replace its conversation without replacing this session object.
|
|
708
|
+
// Bind each admitted run to its captured conversation, including durable resumes;
|
|
709
|
+
// a Zen client must never generate a fresh Go session for each model call.
|
|
710
|
+
const providerForSession = (sessionId) => primary.id === 'zen' || primary.id === 'zen-go'
|
|
711
|
+
? constructProvider(primary.id, det, model, { sessionId })
|
|
712
|
+
: provider;
|
|
698
713
|
// Session-owned discovery with one drain cursor per run. A child shares the
|
|
699
714
|
// discovered scopes without being able to consume the parent's update, and
|
|
700
715
|
// an edit takes effect in this session rather than only after reconnecting.
|
|
@@ -874,9 +889,6 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
874
889
|
computerUseReady: computerUseHost !== undefined,
|
|
875
890
|
...(computerUseError ? { computerUseError } : {}),
|
|
876
891
|
});
|
|
877
|
-
// This session passes a `taskStore` to query() below, which registers the
|
|
878
|
-
// task tools deferred — so `search_tools` has something to find here.
|
|
879
|
-
registry.register([SearchToolsTool]);
|
|
880
892
|
// Web reach, opted into in the config file and nowhere else. The parent's
|
|
881
893
|
// registry only: a child's config carries no provider, and a tool whose
|
|
882
894
|
// provider is missing is a tool that reports itself unwired — truthful,
|
|
@@ -891,6 +903,18 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
891
903
|
// Best-effort — if the runtime can't stand up, the chat still works.
|
|
892
904
|
const delegationScopes = new Map();
|
|
893
905
|
const delegatedInputWaiters = new Map();
|
|
906
|
+
if (options.conversationSessions) {
|
|
907
|
+
const sessions = options.conversationSessions;
|
|
908
|
+
for (const build of [buildConversationSearchTool, buildConversationReadTool])
|
|
909
|
+
registry.register(build((context) => {
|
|
910
|
+
const owner = delegationScopes.get(context.runId);
|
|
911
|
+
if (!owner ||
|
|
912
|
+
owner.projectId !== sessions.projectId ||
|
|
913
|
+
owner.tenantId !== sessions.tenantId)
|
|
914
|
+
throw new Error('The requesting run does not own this conversation.');
|
|
915
|
+
return { sessions, sessionId: owner.sessionId };
|
|
916
|
+
}));
|
|
917
|
+
}
|
|
894
918
|
let subagentRuntime;
|
|
895
919
|
// Stays empty when the runtime below throws, which is the honest answer: the
|
|
896
920
|
// catch is non-fatal and the session then genuinely has no delegate to
|
|
@@ -950,7 +974,12 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
950
974
|
// child announcing a swap the parent never made, inside a tool result,
|
|
951
975
|
// is a worse surface than the child simply failing and the parent
|
|
952
976
|
// reporting it.
|
|
953
|
-
buildProvider: () =>
|
|
977
|
+
buildProvider: (sessionId) => {
|
|
978
|
+
if (!sessionId && (primary.id === 'zen' || primary.id === 'zen-go')) {
|
|
979
|
+
throw new Error('A delegated provider requires its invoking conversation.');
|
|
980
|
+
}
|
|
981
|
+
return constructProvider(primary.id, det ? { ...det, apiKey: currentToken ?? det.apiKey } : det, model, { sessionId });
|
|
982
|
+
},
|
|
954
983
|
buildTools: () => {
|
|
955
984
|
// Sub-agents get the parent's working set minus `search_tools`:
|
|
956
985
|
// they run without a task store, so nothing in their registry is
|
|
@@ -967,6 +996,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
967
996
|
});
|
|
968
997
|
subagentRuntime = sub;
|
|
969
998
|
registry.register([sub.agentTool, sub.waitForTaskTool]);
|
|
999
|
+
if (sub.sendMessageTool)
|
|
1000
|
+
registry.register(sub.sendMessageTool);
|
|
970
1001
|
allowedAgentIds = sub.allowedAgentIds;
|
|
971
1002
|
}
|
|
972
1003
|
catch (err) {
|
|
@@ -980,6 +1011,20 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
980
1011
|
// and got none had nothing on stderr to say why.
|
|
981
1012
|
cliLogger().warn('sub-agent runtime unavailable this session', exceptionAttributes(err));
|
|
982
1013
|
}
|
|
1014
|
+
// This capability belongs to the active main turn, never the child roster.
|
|
1015
|
+
const modelSwitchHandlers = new Map();
|
|
1016
|
+
if (options.allowModelSwitch) {
|
|
1017
|
+
registry.register(buildSwitchModelTool(async (request, context) => {
|
|
1018
|
+
const handler = modelSwitchHandlers.get(context.runId);
|
|
1019
|
+
if (!handler || context.abortSignal?.aborted) {
|
|
1020
|
+
return {
|
|
1021
|
+
kind: 'rejected',
|
|
1022
|
+
reason: 'This turn no longer owns model selection.',
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
return handler(request, context.abortSignal);
|
|
1026
|
+
}));
|
|
1027
|
+
}
|
|
983
1028
|
// `ask_user_question`, where somebody can answer. The SDK tool parks the
|
|
984
1029
|
// run through the handler it was BUILT with, so that handler reads the
|
|
985
1030
|
// turn's answerer through a holder the prelude fills: the tool is per
|
|
@@ -1021,8 +1066,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1021
1066
|
// query call, because the doctrine tells the model to plan with them and a
|
|
1022
1067
|
// tool it must search for first is a tool it skips.
|
|
1023
1068
|
//
|
|
1024
|
-
//
|
|
1025
|
-
//
|
|
1069
|
+
// A tool server or plugin can still register a deferred roster; query()
|
|
1070
|
+
// mounts search_tools when one exists. Do not advertise an empty search. The
|
|
1026
1071
|
// task tools are registered inside query(), after this function returns,
|
|
1027
1072
|
// which is why the connect line reports no count of them — counting here
|
|
1028
1073
|
// would mean restating query's registration order in the CLI.
|
|
@@ -1067,16 +1112,9 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1067
1112
|
// Persists across turns: once the user picks "approve all", later tool
|
|
1068
1113
|
// batches in this session run without prompting.
|
|
1069
1114
|
const approval = { all: false };
|
|
1070
|
-
//
|
|
1071
|
-
//
|
|
1072
|
-
//
|
|
1073
|
-
// index for every message.
|
|
1074
|
-
//
|
|
1075
|
-
// Unconditional, unlike the answer gate. A gate changes what a run may do
|
|
1076
|
-
// and so must be asked for; promotion changes only what survives it, and
|
|
1077
|
-
// the alternative — the run's own extracted knowledge being discarded at
|
|
1078
|
-
// settle — is what this repository has been doing all along by accident.
|
|
1079
|
-
// A run that learned nothing still writes nothing.
|
|
1115
|
+
// Share the project store with tools and recall. Each run selects either
|
|
1116
|
+
// this extracted-claim promoter or explicit consolidation, never both.
|
|
1117
|
+
// Candidates without useful claims write nothing.
|
|
1080
1118
|
const promoteMemory = createMemoryPromoter({ store: memoryStore });
|
|
1081
1119
|
// Plugins are the last fallible startup resource. The ordering is ownership:
|
|
1082
1120
|
// a malformed MCP entry cannot strand imported plugin hooks, and a plugin
|
|
@@ -1155,10 +1193,25 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1155
1193
|
let reasoningEffortDefault;
|
|
1156
1194
|
let effortNotice;
|
|
1157
1195
|
try {
|
|
1158
|
-
const
|
|
1196
|
+
const capabilityMembers = [
|
|
1159
1197
|
{ provider, model },
|
|
1160
|
-
...fallbackPlan.build(currentToken),
|
|
1161
|
-
]
|
|
1198
|
+
...fallbackPlan.build(currentToken, scope.sessionId),
|
|
1199
|
+
];
|
|
1200
|
+
const capabilityView = withProviderFallback(await Promise.all(capabilityMembers.map(async (member) => {
|
|
1201
|
+
const memberModel = member.model ?? model;
|
|
1202
|
+
const known = member.provider.reasoningEffortLevelsFor
|
|
1203
|
+
? member.provider.reasoningEffortLevelsFor(memberModel)
|
|
1204
|
+
: member.provider.effortLevelsFor?.(memberModel);
|
|
1205
|
+
// A model-owned answer (including []) needs no extra catalogue
|
|
1206
|
+
// request. Discover only missing capability information.
|
|
1207
|
+
const catalogue = known === undefined && member.provider.listModels
|
|
1208
|
+
? await runPickerProviderOperation(undefined, (signal) => member.provider.listModels?.(signal) ?? Promise.resolve([])).catch(() => [])
|
|
1209
|
+
: [];
|
|
1210
|
+
return {
|
|
1211
|
+
...member,
|
|
1212
|
+
provider: modelReasoningView(member.provider, memberModel, catalogue),
|
|
1213
|
+
};
|
|
1214
|
+
})));
|
|
1162
1215
|
const offered = capabilityView.reasoningEffortLevelsFor
|
|
1163
1216
|
? capabilityView.reasoningEffortLevelsFor(model)
|
|
1164
1217
|
: capabilityView.effortLevelsFor?.(model);
|
|
@@ -1199,7 +1252,10 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1199
1252
|
const pluginSkills = pluginRuntime
|
|
1200
1253
|
? await currentPluginSkills(pluginRuntime.skills)
|
|
1201
1254
|
: undefined;
|
|
1202
|
-
const
|
|
1255
|
+
const curatedMemory = readMemory(undefined, cwd);
|
|
1256
|
+
for (const notice of formatMemoryDiagnostics(curatedMemory))
|
|
1257
|
+
cliLogger().warn(notice);
|
|
1258
|
+
const memoryPrompt = composeMemoryPrompt(curatedMemory);
|
|
1203
1259
|
const environmentPrompt = composeEnvironmentPrompt({
|
|
1204
1260
|
...(await readEnvironmentFacts(cwd)),
|
|
1205
1261
|
additionalDirectories: [...directories],
|
|
@@ -1219,15 +1275,16 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1219
1275
|
}
|
|
1220
1276
|
delegatedResumeHandlers.set(entry.runId, resumeHandler);
|
|
1221
1277
|
delegationScopes.set(entry.runId, { ...entry, topicId: scope.topicId });
|
|
1278
|
+
const runTaskStore = selectTaskStore(entry.runId, { ...entry, topicId: scope.topicId });
|
|
1222
1279
|
try {
|
|
1223
1280
|
return await resumeRun({
|
|
1224
|
-
provider,
|
|
1225
|
-
fallbackProviders: fallbackPlan.build(currentToken),
|
|
1281
|
+
provider: providerForSession(entry.sessionId),
|
|
1282
|
+
fallbackProviders: fallbackPlan.build(currentToken, entry.sessionId),
|
|
1226
1283
|
tools: registry,
|
|
1227
1284
|
pluginManager: pluginRuntime?.manager,
|
|
1228
1285
|
skillRegistry: pluginRuntime?.skills,
|
|
1229
1286
|
skills: pluginSkills,
|
|
1230
|
-
taskStore:
|
|
1287
|
+
taskStore: runTaskStore,
|
|
1231
1288
|
// The same availability the original run registered under.
|
|
1232
1289
|
// A resumed run re-registers the task tools; leaving them at
|
|
1233
1290
|
// the kernel's `deferred` default would hand the model a plan
|
|
@@ -1244,6 +1301,21 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1244
1301
|
: {}),
|
|
1245
1302
|
authorizationGate: gateFor(options.rules),
|
|
1246
1303
|
compactionConfig: compactionConfigFor(options.compaction),
|
|
1304
|
+
prepareStep: [
|
|
1305
|
+
createTaskContextStep(runTaskStore, entry.tenantId),
|
|
1306
|
+
...(options.memory?.recall === false
|
|
1307
|
+
? []
|
|
1308
|
+
: [
|
|
1309
|
+
createMemoryRecallStep({
|
|
1310
|
+
store: memoryStore,
|
|
1311
|
+
identifierGrounding: options.memory?.identifierGrounding,
|
|
1312
|
+
}),
|
|
1313
|
+
]),
|
|
1314
|
+
...(options.conversationSessions ? [createContextInventoryStep()] : []),
|
|
1315
|
+
],
|
|
1316
|
+
...(options.compaction?.consolidate
|
|
1317
|
+
? { consolidateInto: memoryStore }
|
|
1318
|
+
: { promoteMemory }),
|
|
1247
1319
|
projectInstructionContext: projectInstructions.createRunContext(),
|
|
1248
1320
|
pathBuilder,
|
|
1249
1321
|
...(sandbox.provider ? { sandboxProvider: sandbox.provider } : {}),
|
|
@@ -1391,11 +1463,12 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1391
1463
|
reasoningEffortLevels,
|
|
1392
1464
|
reasoningEffortDefault,
|
|
1393
1465
|
compact: (messages) => operations.promise(undefined, async (signal) => {
|
|
1466
|
+
const sessionId = scope.sessionId;
|
|
1394
1467
|
await prepareProviderCredential(signal);
|
|
1395
1468
|
return compactNow({
|
|
1396
1469
|
messages,
|
|
1397
1470
|
config: compactionConfigFor(options.compaction),
|
|
1398
|
-
provider,
|
|
1471
|
+
provider: providerForSession(sessionId),
|
|
1399
1472
|
model,
|
|
1400
1473
|
signal,
|
|
1401
1474
|
});
|
|
@@ -1459,6 +1532,8 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1459
1532
|
throw new Error(`Run ${runId} already owns a delegated review channel.`);
|
|
1460
1533
|
}
|
|
1461
1534
|
delegatedResumeHandlers.set(runId, resumeHandler);
|
|
1535
|
+
if (opts?.onModelSwitch)
|
|
1536
|
+
modelSwitchHandlers.set(runId, opts.onModelSwitch);
|
|
1462
1537
|
if (opts?.waitForInbound)
|
|
1463
1538
|
delegatedInputWaiters.set(runId, opts.waitForInbound);
|
|
1464
1539
|
const turnScope = { ...scope };
|
|
@@ -1481,7 +1556,11 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1481
1556
|
const pluginSkills = pluginRuntime
|
|
1482
1557
|
? await currentPluginSkills(pluginRuntime.skills)
|
|
1483
1558
|
: undefined;
|
|
1484
|
-
const
|
|
1559
|
+
const curatedMemory = readMemory(undefined, cwd);
|
|
1560
|
+
for (const notice of formatMemoryDiagnostics(curatedMemory)) {
|
|
1561
|
+
yield { kind: 'context', text: notice, shed: false };
|
|
1562
|
+
}
|
|
1563
|
+
const memoryPrompt = composeMemoryPrompt(curatedMemory);
|
|
1485
1564
|
currentOnQuestion = opts?.onQuestion;
|
|
1486
1565
|
const [environmentFacts, turnSnapshot] = await Promise.all([
|
|
1487
1566
|
readEnvironmentFacts(cwd),
|
|
@@ -1553,9 +1632,10 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1553
1632
|
capturedAuthority = Object.freeze({ ...opts.goalRound });
|
|
1554
1633
|
goalAuthorities.set(opts.runId, capturedAuthority);
|
|
1555
1634
|
}
|
|
1635
|
+
const runTaskStore = selectTaskStore(runId, turnScope);
|
|
1556
1636
|
try {
|
|
1557
1637
|
yield* runTurn({
|
|
1558
|
-
provider,
|
|
1638
|
+
provider: providerForSession(turnScope.sessionId),
|
|
1559
1639
|
compactionConfig: compactionConfigFor(options.compaction),
|
|
1560
1640
|
...(options.compaction?.consolidate ? { consolidateInto: memoryStore } : {}),
|
|
1561
1641
|
...(jobRegistry
|
|
@@ -1571,7 +1651,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1571
1651
|
// that expired hours ago — and a chain whose own members are stale
|
|
1572
1652
|
// is a fallback that fails for the reason the fallback exists to
|
|
1573
1653
|
// survive. Building a driver is a client object, not a request.
|
|
1574
|
-
fallbackProviders: fallbackPlan.build(currentToken),
|
|
1654
|
+
fallbackProviders: fallbackPlan.build(currentToken, turnScope.sessionId),
|
|
1575
1655
|
model,
|
|
1576
1656
|
tools: registry,
|
|
1577
1657
|
pluginManager: pluginRuntime?.manager,
|
|
@@ -1584,10 +1664,24 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1584
1664
|
...(options.limits ? { limits: options.limits } : {}),
|
|
1585
1665
|
sandboxWorkspace,
|
|
1586
1666
|
rules: options.rules,
|
|
1667
|
+
structuredOutput: options.structuredOutput,
|
|
1587
1668
|
reviewAnswer: options.reviewAnswer,
|
|
1588
1669
|
maxAnswerReviews: options.maxAnswerReviews,
|
|
1589
|
-
promoteMemory,
|
|
1590
|
-
|
|
1670
|
+
promoteMemory: options.compaction?.consolidate ? undefined : promoteMemory,
|
|
1671
|
+
prepareStep: [
|
|
1672
|
+
createTaskContextStep(runTaskStore, turnScope.tenantId),
|
|
1673
|
+
...(options.memory?.recall === false
|
|
1674
|
+
? []
|
|
1675
|
+
: [
|
|
1676
|
+
createMemoryRecallStep({
|
|
1677
|
+
store: memoryStore,
|
|
1678
|
+
query: lastUserText(messages),
|
|
1679
|
+
identifierGrounding: options.memory?.identifierGrounding,
|
|
1680
|
+
}),
|
|
1681
|
+
]),
|
|
1682
|
+
...(options.conversationSessions ? [createContextInventoryStep()] : []),
|
|
1683
|
+
],
|
|
1684
|
+
taskStore: runTaskStore,
|
|
1591
1685
|
systemPrompt,
|
|
1592
1686
|
messages,
|
|
1593
1687
|
projectInstructionContext: projectInstructions.createRunContext(),
|
|
@@ -1625,6 +1719,7 @@ export async function createAgentSession(prefs, detected, options = {}) {
|
|
|
1625
1719
|
finally {
|
|
1626
1720
|
if (delegatedResumeHandlers.get(runId) === resumeHandler) {
|
|
1627
1721
|
delegatedResumeHandlers.delete(runId);
|
|
1722
|
+
modelSwitchHandlers.delete(runId);
|
|
1628
1723
|
delegatedInputWaiters.delete(runId);
|
|
1629
1724
|
delegationScopes.delete(runId);
|
|
1630
1725
|
await subagentRuntime?.releaseRun(runId);
|
|
@@ -1656,7 +1751,8 @@ function planFallbacks(members, detected) {
|
|
|
1656
1751
|
continue;
|
|
1657
1752
|
}
|
|
1658
1753
|
const det = findDetected(detected, member.id);
|
|
1659
|
-
if (entry.
|
|
1754
|
+
if (requiresCredentialForModel(entry, member.model ?? entry.defaultModel) &&
|
|
1755
|
+
!hasApiCredential(entry, det?.apiKey)) {
|
|
1660
1756
|
notices.push(`Provider chain: ${position} (${entry.label}) has no credential, so nothing will fall over to it. ` +
|
|
1661
1757
|
`Set one of: ${entry.envVars.join(', ')}.`);
|
|
1662
1758
|
continue;
|
|
@@ -1671,7 +1767,7 @@ function planFallbacks(members, detected) {
|
|
|
1671
1767
|
}
|
|
1672
1768
|
return {
|
|
1673
1769
|
notices,
|
|
1674
|
-
build(headToken) {
|
|
1770
|
+
build(headToken, sessionId) {
|
|
1675
1771
|
const out = [];
|
|
1676
1772
|
for (const { choice, det } of usable) {
|
|
1677
1773
|
const entry = PROVIDER_REGISTRY[choice.id];
|
|
@@ -1681,7 +1777,9 @@ function planFallbacks(members, detected) {
|
|
|
1681
1777
|
try {
|
|
1682
1778
|
const credential = headToken !== undefined && det?.oauth ? { ...det, apiKey: headToken } : det;
|
|
1683
1779
|
out.push({
|
|
1684
|
-
provider: constructProvider(choice.id, credential, memberModel
|
|
1780
|
+
provider: constructProvider(choice.id, credential, memberModel, {
|
|
1781
|
+
sessionId,
|
|
1782
|
+
}),
|
|
1685
1783
|
model: memberModel,
|
|
1686
1784
|
});
|
|
1687
1785
|
}
|
|
@@ -1697,7 +1795,7 @@ function planFallbacks(members, detected) {
|
|
|
1697
1795
|
},
|
|
1698
1796
|
};
|
|
1699
1797
|
}
|
|
1700
|
-
export function constructProvider(id, det, model) {
|
|
1798
|
+
export function constructProvider(id, det, model, context = {}) {
|
|
1701
1799
|
switch (id) {
|
|
1702
1800
|
case 'anthropic': {
|
|
1703
1801
|
const token = det?.apiKey ?? '';
|
|
@@ -1749,6 +1847,24 @@ export function constructProvider(id, det, model) {
|
|
|
1749
1847
|
});
|
|
1750
1848
|
return provider;
|
|
1751
1849
|
}
|
|
1850
|
+
case 'zen':
|
|
1851
|
+
case 'zen-go': {
|
|
1852
|
+
const apiKey = hasApiCredential(PROVIDER_REGISTRY[id], det?.apiKey) ? det?.apiKey : undefined;
|
|
1853
|
+
if (id === 'zen-go' && apiKey === undefined)
|
|
1854
|
+
throw new Error('Zen Go requires an API key.');
|
|
1855
|
+
const { provider } = ProviderRegistry.create({
|
|
1856
|
+
...(id === 'zen-go'
|
|
1857
|
+
? { type: 'zen-go', apiKey: apiKey }
|
|
1858
|
+
: {
|
|
1859
|
+
type: 'zen',
|
|
1860
|
+
...(apiKey === undefined ? {} : { apiKey }),
|
|
1861
|
+
}),
|
|
1862
|
+
baseURL: det?.baseUrl,
|
|
1863
|
+
model,
|
|
1864
|
+
...(context.sessionId ? { sessionId: context.sessionId } : {}),
|
|
1865
|
+
});
|
|
1866
|
+
return provider;
|
|
1867
|
+
}
|
|
1752
1868
|
case 'ollama': {
|
|
1753
1869
|
const { provider } = ProviderRegistry.create({
|
|
1754
1870
|
type: 'ollama',
|
|
@@ -2013,7 +2129,7 @@ function compactionConfigFor(compaction) {
|
|
|
2013
2129
|
: {}),
|
|
2014
2130
|
};
|
|
2015
2131
|
}
|
|
2016
|
-
async function* runTurn({ provider, compactionConfig, consolidateInto, backgroundJobs, backgroundJobOwner, fallbackProviders, model, tools, pluginManager, skillRegistry, skills, scope, pathBuilder, workingDirectory, limits, additionalDirectories, sandboxWorkspace, rules, reviewAnswer, maxAnswerReviews, promoteMemory, resumeHandler, taskStore, systemPrompt, messages, projectInstructionContext, opts, taskGateway, completionInbox, promptContributions, runtimeToolOverrides, web, sandboxProvider, sandboxTeardownTimeoutMs, onRunEvent, }) {
|
|
2132
|
+
async function* runTurn({ provider, compactionConfig, consolidateInto, backgroundJobs, backgroundJobOwner, fallbackProviders, model, tools, pluginManager, skillRegistry, skills, scope, pathBuilder, workingDirectory, limits, additionalDirectories, sandboxWorkspace, rules, structuredOutput, reviewAnswer, maxAnswerReviews, promoteMemory, prepareStep, resumeHandler, taskStore, systemPrompt, messages, projectInstructionContext, opts, taskGateway, completionInbox, promptContributions, runtimeToolOverrides, web, sandboxProvider, sandboxTeardownTimeoutMs, onRunEvent, }) {
|
|
2017
2133
|
const signal = opts?.signal;
|
|
2018
2134
|
// One presenter for the whole stream, built from the registry this scope
|
|
2019
2135
|
// already holds. Its absence HERE is what forced presentation to be name
|
|
@@ -2022,6 +2138,7 @@ async function* runTurn({ provider, compactionConfig, consolidateInto, backgroun
|
|
|
2022
2138
|
const presenter = createToolPresenter(tools);
|
|
2023
2139
|
try {
|
|
2024
2140
|
const events = query({
|
|
2141
|
+
...(structuredOutput ? { structuredOutput } : {}),
|
|
2025
2142
|
provider,
|
|
2026
2143
|
pathBuilder,
|
|
2027
2144
|
...(opts?.runId ? { runId: opts.runId } : {}),
|
|
@@ -2068,11 +2185,9 @@ async function* runTurn({ provider, compactionConfig, consolidateInto, backgroun
|
|
|
2068
2185
|
// that shipped before gates existed.
|
|
2069
2186
|
...(reviewAnswer ? { reviewAnswer } : {}),
|
|
2070
2187
|
...(maxAnswerReviews !== undefined ? { maxAnswerReviews } : {}),
|
|
2071
|
-
//
|
|
2072
|
-
// do and so must be asked for; promotion changes only what
|
|
2073
|
-
// survives it, and a run that learned nothing still writes
|
|
2074
|
-
// nothing.
|
|
2188
|
+
// Undefined when the host selected consolidation as its writer.
|
|
2075
2189
|
promoteMemory,
|
|
2190
|
+
...(prepareStep ? { prepareStep } : {}),
|
|
2076
2191
|
agentId: 'namzu',
|
|
2077
2192
|
agentName: 'namzu',
|
|
2078
2193
|
...(systemPrompt ? { systemPrompt } : {}),
|
|
@@ -2347,6 +2462,7 @@ export function toAgentEvent(event, presenter) {
|
|
|
2347
2462
|
// answer was refused.
|
|
2348
2463
|
return {
|
|
2349
2464
|
kind: 'done',
|
|
2465
|
+
text: event.result,
|
|
2350
2466
|
...(event.budget ? { budget: event.budget } : {}),
|
|
2351
2467
|
...(event.stopReason ? { stopReason: event.stopReason } : {}),
|
|
2352
2468
|
};
|