@inneranimalmedia/agentsam-sdk 2.4.1 → 2.6.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/AGENTSAM.md +55 -0
- package/README.md +17 -12
- package/bin/agentsam +2 -0
- package/docs/AGENTSAM_ASTRA_OPENAI_INTEGRATION.md +1363 -0
- package/docs/AGENTSAM_WORKBENCH.md +30 -0
- package/docs/AUTH_IDENTITY_CONTRACT.md +52 -0
- package/docs/CAPABILITIES.md +5 -3
- package/docs/CLI_SHELL.md +167 -80
- package/docs/CMS_STUDIO.md +101 -0
- package/docs/CONTEXT.md +170 -0
- package/docs/LOCAL_OLLAMA.md +58 -0
- package/docs/MERKLE.md +19 -19
- package/docs/PORTABLE_CONTEXT.md +4 -3
- package/docs/PROJECT_CONFIG.md +72 -0
- package/docs/RELEASES.md +35 -2
- package/docs/REPOSITORY_INTELLIGENCE.md +1 -1
- package/docs/REPOSITORY_KNOWLEDGE.md +114 -0
- package/docs/SDK_WORKER.md +86 -0
- package/docs/SECURITY.md +59 -22
- package/docs/client-cms-editor.md +15 -0
- package/docs/local-studio/WORKMODE_DONOR_NOTES.md +485 -0
- package/package.json +35 -10
- package/packages/connectors/cloudflare/package.json +10 -0
- package/packages/connectors/cloudflare/src/index.js +127 -0
- package/packages/connectors/cloudflare/src/owner.js +76 -0
- package/packages/connectors/cloudflare/src/routes.js +223 -0
- package/packages/connectors/cloudflare/src/vault.js +80 -0
- package/packages/connectors/cloudflare/tests/connector.test.mjs +44 -0
- package/packages/identity/package.json +10 -1
- package/packages/identity/src/contracts/auth-config.js +109 -0
- package/packages/identity/src/index.js +1 -0
- package/packages/identity/src/oauth/README.md +2 -2
- package/packages/identity/src/oauth/credentials.js +11 -4
- package/packages/identity/src/oauth/iam-platform.js +3 -3
- package/packages/identity/src/providers/iam/index.js +7 -7
- package/packages/identity/src/providers/iam/oauth.js +6 -4
- package/packages/identity/src/providers/iam/profile.js +5 -5
- package/packages/identity/tests/auth-config.test.mjs +61 -0
- package/packages/identity/tests/oauth-credentials.test.mjs +13 -2
- package/protocol/FILEMETA_V1.md +95 -0
- package/protocol/INSPECT_VIEWS_V1.md +40 -0
- package/protocol/MERKLE_PERSISTENCE_V1.md +50 -0
- package/protocol/MERKLE_V1.md +3 -1
- package/protocol/README.md +1 -0
- package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +19 -0
- package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +13 -0
- package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +19 -0
- package/protocol/capabilities/manifest.json +79 -3
- package/protocol/capabilities/repository-snapshot.schema.json +1 -0
- package/protocol/context/context-budget.schema.json +36 -0
- package/protocol/context/context-item.schema.json +19 -0
- package/protocol/context/resolved-context-pack.schema.json +47 -0
- package/protocol/context/result-policy.schema.json +17 -0
- package/protocol/knowledge/context-pack.schema.json +33 -0
- package/protocol/knowledge/retrieval-query.schema.json +68 -13
- package/protocol/models/README.md +373 -0
- package/protocol/models/model-inventory-v2.schema.json +212 -0
- package/python/README.md +15 -7
- package/python/agentsam_sdk/cli.py +0 -21
- package/python/agentsam_sdk/tui/README.md +17 -12
- package/python/agentsam_sdk/tui/bootstrap.py +2 -2
- package/python/agentsam_sdk/tui/demo.py +25 -10
- package/python/agentsam_sdk/tui/onboarding.py +208 -0
- package/python/tests/test_tui_cli.py +7 -6
- package/skills/README.md +21 -0
- package/skills/agentsam-app-fundamentals/SKILL.md +165 -0
- package/skills/agentsam-app-fundamentals/references/graphs-contracts-ast-merkle.md +89 -0
- package/skills/agentsam-app-fundamentals/references/trust-credentials-and-destinations.md +99 -0
- package/skills/agentsam-cloudflare-workers/SKILL.md +53 -0
- package/skills/agentsam-cloudflare-workers/references/cpu-profiling.md +16 -0
- package/skills/agentsam-cloudflare-workers/references/errors-and-observability.md +29 -0
- package/skills/agentsam-cloudflare-workers/references/wrangler-native-map.md +28 -0
- package/skills/agentsam-jr-dev/SKILL.md +232 -0
- package/skills/agentsam-jr-dev/references/real-application-logic.md +156 -0
- package/skills/agentsam-jr-dev/references/web-application-fundamentals.md +240 -0
- package/skills/agentsam-progression-guard/SKILL.md +197 -0
- package/skills/agentsam-progression-guard/references/checkpoint-chain.md +111 -0
- package/skills/agentsam-progression-guard/references/hooks-operational-io.md +96 -0
- package/skills/catalog.json +71 -0
- package/src/agent/capability-adapter.js +25 -13
- package/src/agent/index.js +1 -0
- package/src/agent/responses-runner.js +325 -0
- package/src/capabilities/index.js +7 -0
- package/src/capabilities/repository-snapshot-view.js +238 -0
- package/src/capabilities/repository-snapshot.js +28 -14
- package/src/cli.js +198 -76
- package/src/cloudflare/cpu-profile.js +115 -0
- package/src/cloudflare/index.js +14 -0
- package/src/cloudflare/wrangler.js +132 -0
- package/src/commands/account-auth.js +47 -0
- package/src/commands/cloudflare.js +58 -0
- package/src/commands/connections.js +93 -0
- package/src/commands/context-economics.js +114 -0
- package/src/commands/context.js +14 -2
- package/src/commands/db.js +4 -7
- package/src/commands/deploy.js +54 -32
- package/src/commands/eval.js +63 -0
- package/src/commands/interactive.js +18 -0
- package/src/commands/knowledge.js +27 -4
- package/src/commands/merkle-persist.js +118 -0
- package/src/commands/merkle.js +32 -17
- package/src/commands/models.js +152 -0
- package/src/commands/ollama.js +259 -0
- package/src/commands/preferences.js +144 -0
- package/src/commands/product.js +86 -16
- package/src/commands/resume.js +67 -0
- package/src/commands/security.js +8 -6
- package/src/commands/shell.js +405 -100
- package/src/commands/skills.js +66 -0
- package/src/commands/start-local.js +1 -1
- package/src/commands/tunnel.js +5 -4
- package/src/commands/whoami.js +86 -0
- package/src/context/budget.js +120 -0
- package/src/context/compact.js +28 -0
- package/src/context/index.js +7 -0
- package/src/context/rehydrate.js +35 -0
- package/src/context/resolve.js +116 -0
- package/src/context/result-policy.js +66 -0
- package/src/errors/diagnostic.js +160 -0
- package/src/errors/index.js +9 -0
- package/src/eval/context.js +191 -0
- package/src/eval/index.js +1 -0
- package/src/index.js +70 -0
- package/src/indexing/execution-boundary.js +144 -0
- package/src/indexing/index.js +8 -0
- package/src/indexing/provider.js +41 -0
- package/src/knowledge/config.js +2 -2
- package/src/knowledge/context-pack.js +12 -1
- package/src/knowledge/contracts.js +9 -4
- package/src/knowledge/engine.js +7 -3
- package/src/knowledge/service/server.js +1 -1
- package/src/lib/account-session.js +98 -0
- package/src/lib/agent-instructions.js +73 -0
- package/src/lib/auth.js +4 -0
- package/src/lib/bridge-client.js +7 -5
- package/src/lib/cli-preferences.js +78 -0
- package/src/lib/core-client.js +8 -8
- package/src/lib/deploy/git-guard.js +69 -0
- package/src/lib/deploy/health.js +57 -0
- package/src/lib/deploy/local-studio.js +283 -0
- package/src/lib/deploy/secret-scan.js +65 -0
- package/src/lib/deploy-receipt/index.js +5 -2
- package/src/lib/detect-context.js +6 -5
- package/src/lib/execution-approvals.js +59 -0
- package/src/lib/identity-scaffold.js +1 -1
- package/src/lib/local-scaffold.js +35 -33
- package/src/lib/local-sessions.js +127 -0
- package/src/lib/local-status.js +9 -17
- package/src/lib/merkle/cloudflare-persistence.js +321 -0
- package/src/lib/merkle/filemeta.js +43 -0
- package/src/lib/merkle/git-ignore.js +24 -0
- package/src/lib/merkle/hash.js +1 -0
- package/src/lib/merkle/index.js +22 -0
- package/src/lib/merkle/persistence.js +72 -0
- package/src/lib/merkle/semantic.js +359 -0
- package/src/lib/merkle/snapshot.js +9 -3
- package/src/lib/merkle/tree.js +11 -6
- package/src/lib/project-config.js +227 -0
- package/src/lib/project-rules.js +68 -0
- package/src/lib/provider-credentials.js +83 -0
- package/src/lib/save-sdk-token.js +1 -1
- package/src/lib/scaffold/templates/worker-api/index.js +101 -20
- package/src/lib/scaffold/wizards/worker-api.js +27 -11
- package/src/lib/slash-commands.js +22 -15
- package/src/lib/tools.js +11 -5
- package/src/models/catalog.js +135 -0
- package/src/models/index.js +7 -0
- package/src/providers/index.js +5 -0
- package/src/providers/openai-responses.js +275 -0
- package/src/security/index.js +1 -0
- package/src/security/inventory.js +4 -1
- package/src/security/process.js +35 -9
- package/src/security/render.js +27 -5
- package/src/security/scan.js +24 -9
- package/src/security/trust-boundary.js +24 -0
- package/src/skills/index.js +64 -0
- package/src/telemetry/contracts.js +203 -0
- package/src/telemetry/events.js +48 -0
- package/src/telemetry/index.js +8 -0
- package/src/tools/hydrate.js +35 -0
- package/src/tools/index.js +2 -0
- package/src/tools/search.js +70 -0
- package/src/ui/ansi.js +1 -1
- package/src/ui/boot.js +54 -0
- package/src/ui/merkle/render.js +1 -0
- package/src/ui/runtime-activity.js +192 -0
- package/src/ui/theme.js +19 -18
- package/test/account-session.test.mjs +36 -0
- package/test/app-building-skills.test.mjs +61 -0
- package/test/apps-scaffold-contract.test.mjs +56 -0
- package/test/capabilities.test.mjs +53 -4
- package/test/cli-preferences.test.mjs +46 -0
- package/test/cloudflare-connector.test.mjs +96 -0
- package/test/cloudflare-runtime.test.mjs +75 -0
- package/test/context.test.mjs +144 -0
- package/test/deploy-health-scan.test.mjs +67 -0
- package/test/error-diagnostics.test.mjs +59 -0
- package/test/eval-context.test.mjs +37 -0
- package/test/execution-approvals.test.mjs +27 -0
- package/test/indexing-provider.test.mjs +29 -0
- package/test/jr-dev-skill.test.mjs +26 -0
- package/test/knowledge-context-pack.test.mjs +19 -0
- package/test/knowledge.test.mjs +1 -1
- package/test/local-sessions.test.mjs +42 -0
- package/test/local-studio-deploy.test.mjs +83 -0
- package/test/merkle-persistence.test.mjs +91 -0
- package/test/merkle.test.mjs +77 -3
- package/test/model-catalog.test.mjs +43 -0
- package/test/models.test.mjs +51 -0
- package/test/npm10-lock.test.mjs +29 -0
- package/test/ollama.test.mjs +94 -0
- package/test/openai-responses.test.mjs +95 -0
- package/test/project-config.test.mjs +81 -0
- package/test/project-rules.test.mjs +44 -0
- package/test/provider-credentials.test.mjs +52 -0
- package/test/rehydrate.test.mjs +25 -0
- package/test/release-hygiene.test.mjs +34 -0
- package/test/repository-snapshot-view.test.mjs +112 -0
- package/test/responses-runner.test.mjs +148 -0
- package/test/runtime-activity.test.mjs +98 -0
- package/test/sdk-worker-contract.test.mjs +68 -0
- package/test/security.test.mjs +46 -0
- package/test/shell.test.mjs +55 -21
- package/test/skills.test.mjs +22 -0
- package/test/smoke.mjs +5 -2
- package/test/telemetry.test.mjs +79 -0
- package/test/theme-portability.test.mjs +14 -0
- package/test/tools-search.test.mjs +40 -0
- package/test/whoami-resume.test.mjs +56 -0
- package/examples/agentsam-tui-ansi.mjs +0 -149
- package/src/commands/tui.js +0 -120
- package/src/ui/splash-xterm.js +0 -290
- package/src/ui/splash.js +0 -426
package/src/commands/shell.js
CHANGED
|
@@ -1,16 +1,48 @@
|
|
|
1
1
|
import readline from 'node:readline';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
4
5
|
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { confirm, isCancel, select } from '@clack/prompts';
|
|
5
7
|
import { SLASH_COMMANDS, SHELL_PHASES } from '../lib/slash-commands.js';
|
|
6
8
|
import { runContext } from './context.js';
|
|
7
9
|
import { runDb } from './db.js';
|
|
8
10
|
import { runDeploy } from './deploy.js';
|
|
9
11
|
import { runStatus } from './status.js';
|
|
10
|
-
import {
|
|
12
|
+
import { runModels } from './models.js';
|
|
13
|
+
import { configureCliPreferences } from './preferences.js';
|
|
14
|
+
import { runCloudflare } from './cloudflare.js';
|
|
15
|
+
import { createRuntimeActivity } from '../ui/runtime-activity.js';
|
|
16
|
+
import { diagnosticFromError, renderDiagnosticError } from '../errors/index.js';
|
|
17
|
+
import { getModelRecord } from '../models/index.js';
|
|
18
|
+
import { readCliPreferences, updateCliPreferences } from '../lib/cli-preferences.js';
|
|
19
|
+
import { buildContextEconomicsReport, renderContextEconomics } from './context-economics.js';
|
|
20
|
+
import { createOpenAIResponsesAdapter } from '../providers/index.js';
|
|
21
|
+
import { createCapabilityAdapter, runResponsesAgent } from '../agent/index.js';
|
|
22
|
+
import { resolveProviderCredential } from '../lib/provider-credentials.js';
|
|
23
|
+
import { createLocalSession, saveLocalSession, sessionTitleFromInput } from '../lib/local-sessions.js';
|
|
24
|
+
import { grantExecutionApproval, isExecutionApproved, toolApprovalKey } from '../lib/execution-approvals.js';
|
|
25
|
+
import { runWhoami } from './whoami.js';
|
|
11
26
|
|
|
12
|
-
function writeLine(write, value = '') {
|
|
13
|
-
|
|
27
|
+
function writeLine(write, value = '') { write(`${value}\n`); }
|
|
28
|
+
|
|
29
|
+
export function compactCwd(value, home = process.env.HOME || process.env.USERPROFILE || '') {
|
|
30
|
+
const cwd = path.resolve(value);
|
|
31
|
+
const resolvedHome = home ? path.resolve(home) : '';
|
|
32
|
+
if (!resolvedHome) return cwd;
|
|
33
|
+
if (cwd === resolvedHome) return '~';
|
|
34
|
+
return cwd.startsWith(`${resolvedHome}${path.sep}`) ? `~${cwd.slice(resolvedHome.length)}` : cwd;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function shellUsername(env = process.env) {
|
|
38
|
+
const fromEnv = String(env.USER || env.USERNAME || '').trim();
|
|
39
|
+
if (fromEnv) return fromEnv;
|
|
40
|
+
try { return String(os.userInfo().username || '').trim() || 'user'; }
|
|
41
|
+
catch { return 'user'; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function renderShellPrompt(cwd, env = process.env) {
|
|
45
|
+
return `${shellUsername(env)} ${compactCwd(cwd, env.HOME || env.USERPROFILE || '')} > `;
|
|
14
46
|
}
|
|
15
47
|
|
|
16
48
|
export function tokenizeShellLine(input = '') {
|
|
@@ -19,34 +51,22 @@ export function tokenizeShellLine(input = '') {
|
|
|
19
51
|
let token = '';
|
|
20
52
|
let quote = '';
|
|
21
53
|
let started = false;
|
|
22
|
-
|
|
23
54
|
const flush = () => {
|
|
24
55
|
if (!started) return;
|
|
25
56
|
tokens.push(token);
|
|
26
57
|
token = '';
|
|
27
58
|
started = false;
|
|
28
59
|
};
|
|
29
|
-
|
|
30
60
|
for (let i = 0; i < source.length; i += 1) {
|
|
31
61
|
const ch = source[i];
|
|
32
62
|
if (quote) {
|
|
33
|
-
if (ch === quote)
|
|
34
|
-
|
|
35
|
-
} else {
|
|
36
|
-
token += ch;
|
|
37
|
-
}
|
|
38
|
-
started = true;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if (ch === '"' || ch === "'") {
|
|
42
|
-
quote = ch;
|
|
63
|
+
if (ch === quote) quote = '';
|
|
64
|
+
else token += ch;
|
|
43
65
|
started = true;
|
|
44
66
|
continue;
|
|
45
67
|
}
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
68
|
+
if (ch === '"' || ch === "'") { quote = ch; started = true; continue; }
|
|
69
|
+
if (/\s/.test(ch)) { flush(); continue; }
|
|
50
70
|
token += ch;
|
|
51
71
|
started = true;
|
|
52
72
|
}
|
|
@@ -62,15 +82,10 @@ export function renderShellCatalog() {
|
|
|
62
82
|
║ Agent Sam Terminal ║
|
|
63
83
|
╚════════════════════════════════╝
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Rich TUI agentsam tui rich optional richer Python UI
|
|
68
|
-
agentsam tui rich --install
|
|
69
|
-
DB agentsam db status local SQLite
|
|
85
|
+
Current milestone: ${next?.label ?? 'interactive runtime'}
|
|
86
|
+
Type / and press Enter for the scrollable command picker.
|
|
70
87
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Slash commands (${SLASH_COMMANDS.length} registered):
|
|
88
|
+
Slash commands (${SLASH_COMMANDS.length} implemented):
|
|
74
89
|
${rows}
|
|
75
90
|
`;
|
|
76
91
|
}
|
|
@@ -86,90 +101,346 @@ function parseDeployOptions(args, cwd) {
|
|
|
86
101
|
return opts;
|
|
87
102
|
}
|
|
88
103
|
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
function spawnGit(cwd, args) {
|
|
105
|
+
const result = spawnSync('git', args, { cwd, stdio: 'inherit', shell: false });
|
|
106
|
+
if (result.error) throw result.error;
|
|
107
|
+
if (result.status !== 0) throw new Error(`git exited ${result.status}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function selectedModel(cwd) {
|
|
111
|
+
const preferences = readCliPreferences(cwd) || {};
|
|
112
|
+
const model = getModelRecord(preferences.modelPreference);
|
|
113
|
+
if (!model) throw new Error('Select an exact provider-verified model with /model first so Agent Sam can verify supported runtime controls.');
|
|
114
|
+
return { preferences, model };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function chooseReasoning(cwd, args, state) {
|
|
118
|
+
const { preferences, model } = selectedModel(cwd);
|
|
119
|
+
let effort = String(args[0] || '').trim().toLowerCase();
|
|
120
|
+
if (!effort) {
|
|
121
|
+
if (!state.interactive) {
|
|
122
|
+
writeLine(state.write, ` ${model.provider_model_id} reasoning: ${model.reasoning_efforts.join(' | ')}`);
|
|
123
|
+
writeLine(state.write, ` current: ${preferences.reasoningEffort || 'auto'}`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const choice = await select({
|
|
127
|
+
message: `Reasoning level · ${model.provider_model_id}`,
|
|
128
|
+
initialValue: model.reasoning_efforts.includes(preferences.reasoningEffort) ? preferences.reasoningEffort : model.reasoning_efforts[0],
|
|
129
|
+
options: model.reasoning_efforts.map((value) => ({ value, label: value === 'xhigh' ? 'Extra high' : value === 'max' ? 'Max' : value[0].toUpperCase() + value.slice(1) })),
|
|
130
|
+
});
|
|
131
|
+
if (isCancel(choice)) return;
|
|
132
|
+
effort = choice;
|
|
96
133
|
}
|
|
134
|
+
if (!model.reasoning_efforts.includes(effort)) throw new Error(`unsupported reasoning level for ${model.provider_model_id}: ${effort}`);
|
|
135
|
+
updateCliPreferences(cwd, { reasoningEffort: effort });
|
|
136
|
+
writeLine(state.write, ` reasoning → ${effort}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function setServiceTier(cwd, tier, write) {
|
|
140
|
+
const { model } = selectedModel(cwd);
|
|
141
|
+
if (!model.service_tiers.includes(tier)) throw new Error(`${model.provider_model_id} does not declare ${tier} processing support`);
|
|
142
|
+
updateCliPreferences(cwd, { serviceTier: tier });
|
|
143
|
+
const label = tier === 'default' ? 'standard' : tier;
|
|
144
|
+
writeLine(write, ` processing → ${label}`);
|
|
145
|
+
if (tier === 'fast') writeLine(write, ' Fast is a paid latency choice; Agent Sam will account for its model-specific pricing multiplier.');
|
|
146
|
+
if (tier === 'flex') writeLine(write, ' Flex trades latency/capacity availability for lower cost; it is not Batch.');
|
|
97
147
|
}
|
|
98
148
|
|
|
99
|
-
async function
|
|
149
|
+
async function showCommandPicker(state) {
|
|
150
|
+
if (!state.interactive) {
|
|
151
|
+
state.write(renderShellCatalog());
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const choice = await select({
|
|
155
|
+
message: 'Agent Sam commands',
|
|
156
|
+
options: SLASH_COMMANDS.map((row) => ({ value: row.cmd, label: row.cmd, hint: row.description })),
|
|
157
|
+
});
|
|
158
|
+
if (isCancel(choice)) return;
|
|
159
|
+
await dispatchShellLine(choice, state);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function runLocalAgent(goal, write, options = {}) {
|
|
100
163
|
if (!goal) {
|
|
101
164
|
writeLine(write, ' Usage: /agent <goal>');
|
|
102
165
|
writeLine(write, ' Requires the local Agent Sam dev server (default http://127.0.0.1:8787).');
|
|
103
166
|
return;
|
|
104
167
|
}
|
|
105
168
|
const base = String(process.env.AGENTSAM_LOCAL_URL || 'http://127.0.0.1:8787').replace(/\/$/, '');
|
|
169
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
170
|
+
const activity = options.activity || createRuntimeActivity({ write, phase: 'thinking', interactive: options.interactive });
|
|
106
171
|
let response;
|
|
172
|
+
activity.start('thinking');
|
|
107
173
|
try {
|
|
108
|
-
response = await
|
|
109
|
-
method: 'POST',
|
|
110
|
-
headers: { 'content-type': 'application/json' },
|
|
111
|
-
body: JSON.stringify({ message: goal }),
|
|
174
|
+
response = await fetchImpl(`${base}/api/agentsam/message`, {
|
|
175
|
+
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ message: goal }),
|
|
112
176
|
});
|
|
113
177
|
} catch (error) {
|
|
178
|
+
activity.fail('unavailable');
|
|
114
179
|
throw new Error(`local Agent Sam unavailable at ${base} — run \`npm run dev\` first (${error?.message || error})`);
|
|
115
180
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
181
|
+
let text;
|
|
182
|
+
try { text = await response.text(); }
|
|
183
|
+
catch (error) { activity.fail('response error'); throw new Error(`local Agent Sam response could not be read: ${error?.message || error}`); }
|
|
184
|
+
if (!response.ok) { activity.fail(`HTTP ${response.status}`); throw new Error(`local Agent Sam returned HTTP ${response.status}: ${text.slice(0, 400)}`); }
|
|
185
|
+
activity.succeed('done');
|
|
186
|
+
try { writeLine(write, JSON.stringify(JSON.parse(text), null, 2)); }
|
|
187
|
+
catch { writeLine(write, text); }
|
|
123
188
|
}
|
|
124
189
|
|
|
125
190
|
async function showLocalLogs(cwd, write) {
|
|
126
191
|
const dbPath = path.join(cwd, '.agentsam', 'data', 'agentsam.sqlite');
|
|
127
|
-
if (!fs.existsSync(dbPath)) {
|
|
128
|
-
writeLine(write, ' No local Agent Sam DB found. Run `agentsam init . --yes` first.');
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
192
|
+
if (!fs.existsSync(dbPath)) { writeLine(write, ' No local Agent Sam DB found. Run `agentsam init . --yes` first.'); return; }
|
|
131
193
|
const { createLocalSqliteDatabase } = await import('../local/sqlite.js');
|
|
132
194
|
const db = await createLocalSqliteDatabase(dbPath);
|
|
133
195
|
try {
|
|
134
|
-
const calls = await db
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
writeLine(write, ' No local Agent Sam tool-call events yet.');
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
writeLine(write, '');
|
|
142
|
-
writeLine(write, ' Recent Agent Sam tool calls');
|
|
143
|
-
for (const row of calls.results) {
|
|
144
|
-
writeLine(write, ` ${String(row.created_at || '').padEnd(20)} ${String(row.status || '').padEnd(10)} ${row.tool_name}`);
|
|
145
|
-
}
|
|
196
|
+
const calls = await db.prepare('SELECT id, session_id, tool_name, status, created_at, completed_at FROM agent_tool_calls ORDER BY created_at DESC LIMIT 20').all();
|
|
197
|
+
if (!calls.results.length) { writeLine(write, ' No local Agent Sam tool-call events yet.'); return; }
|
|
198
|
+
writeLine(write, '\n Recent Agent Sam tool calls');
|
|
199
|
+
for (const row of calls.results) writeLine(write, ` ${String(row.created_at || '').padEnd(20)} ${String(row.status || '').padEnd(10)} ${row.tool_name}`);
|
|
146
200
|
writeLine(write, '');
|
|
147
|
-
} finally {
|
|
148
|
-
|
|
201
|
+
} finally { db.close(); }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function formatCount(value) {
|
|
205
|
+
return Math.max(0, Number(value || 0)).toLocaleString('en-US');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function formatUsd(value) {
|
|
209
|
+
const amount = Number(value || 0);
|
|
210
|
+
if (!Number.isFinite(amount)) return 'unavailable';
|
|
211
|
+
const currency = String.fromCharCode(36);
|
|
212
|
+
if (amount === 0) return currency + '0.000000';
|
|
213
|
+
return currency + (amount < 0.01 ? amount.toFixed(6) : amount.toFixed(4));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function renderSessionReceipt(session) {
|
|
217
|
+
if (!session) return '';
|
|
218
|
+
const usage = session.cumulative_usage || {};
|
|
219
|
+
const input = Number(usage.input_tokens || 0);
|
|
220
|
+
const output = Number(usage.output_tokens || 0);
|
|
221
|
+
const cached = Number(usage.cached_input_tokens || 0);
|
|
222
|
+
const reasoning = Number(usage.reasoning_tokens || 0);
|
|
223
|
+
const total = input + output;
|
|
224
|
+
const active = Number(session.usage_snapshot?.current_context?.input_tokens || 0);
|
|
225
|
+
const lines = [
|
|
226
|
+
'',
|
|
227
|
+
`Token usage: total=${formatCount(total)} input=${formatCount(input)}${cached ? ` (+ ${formatCount(cached)} cached)` : ''} output=${formatCount(output)}${reasoning ? ` reasoning=${formatCount(reasoning)}` : ''}`,
|
|
228
|
+
`Cost: ${formatUsd(session.total_cost_usd)} · ${session.model_key || 'model unavailable'}${session.actual_service_tier ? ` · ${session.actual_service_tier}` : ''}`,
|
|
229
|
+
];
|
|
230
|
+
if (active) lines.push(`Active context: ${formatCount(active)} tokens`);
|
|
231
|
+
lines.push('', 'To continue this session, run:', ` agentsam resume ${session.id}`, '', 'Or run:', ' agentsam resume', '', 'and select:', ` ${session.title || 'this session'}`, '');
|
|
232
|
+
return lines.join('\n');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function persistSession(state, patch = {}) {
|
|
236
|
+
if (!state.session) return null;
|
|
237
|
+
state.session = saveLocalSession({ ...state.session, ...patch }, { home: state.home });
|
|
238
|
+
return state.session;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function recordSessionInput(state, input) {
|
|
242
|
+
const value = String(input || '').trim();
|
|
243
|
+
if (!state.session || !value) return;
|
|
244
|
+
const housekeeping = new Set(['/exit', '/quit', '/session', '/help', '/', '/menu', '/clear']);
|
|
245
|
+
if (housekeeping.has(value.toLowerCase())) return;
|
|
246
|
+
persistSession(state, {
|
|
247
|
+
status: 'active',
|
|
248
|
+
last_input: value,
|
|
249
|
+
title: sessionTitleFromInput(value),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function safeToolInput(value, depth = 0) {
|
|
254
|
+
if (depth > 3) return '[nested]';
|
|
255
|
+
if (Array.isArray(value)) return value.slice(0, 12).map((row) => safeToolInput(row, depth + 1));
|
|
256
|
+
if (!value || typeof value !== 'object') return value;
|
|
257
|
+
const output = {};
|
|
258
|
+
for (const [key, item] of Object.entries(value)) {
|
|
259
|
+
if (/token|secret|password|api[_-]?key|authorization/i.test(key)) output[key] = '[REDACTED]';
|
|
260
|
+
else output[key] = safeToolInput(item, depth + 1);
|
|
261
|
+
}
|
|
262
|
+
return output;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function approveModelRequest(preflight, state) {
|
|
266
|
+
const approvedCeiling = Number(state.session?.approved_projected_call_cost_usd || 0);
|
|
267
|
+
if (approvedCeiling > 0 && preflight.projected_max_call_cost_usd <= approvedCeiling) return true;
|
|
268
|
+
if (!state.interactive) return false;
|
|
269
|
+
writeLine(state.write, '');
|
|
270
|
+
writeLine(state.write, ' Model request');
|
|
271
|
+
writeLine(state.write, ` model ${preflight.model}`);
|
|
272
|
+
writeLine(state.write, ` reasoning ${preflight.reasoning_effort}`);
|
|
273
|
+
writeLine(state.write, ` processing ${preflight.service_tier}`);
|
|
274
|
+
writeLine(state.write, ` context ~${formatCount(preflight.estimated_input_tokens)} input tokens`);
|
|
275
|
+
writeLine(state.write, ` max call ${formatUsd(preflight.projected_max_call_cost_usd)} conservative ceiling`);
|
|
276
|
+
if (Number.isFinite(preflight.tokens_until_pricing_threshold)) writeLine(state.write, ` price cliff ${formatCount(preflight.tokens_until_pricing_threshold)} tokens headroom`);
|
|
277
|
+
const approved = await confirm({ message: 'Send this request?', initialValue: true });
|
|
278
|
+
if (isCancel(approved) || approved !== true) return false;
|
|
279
|
+
persistSession(state, { approved_projected_call_cost_usd: Math.max(approvedCeiling, preflight.projected_max_call_cost_usd) });
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function approveToolExecution(request, state) {
|
|
284
|
+
const sideEffects = request?.descriptor?.side_effects || 'none';
|
|
285
|
+
if (sideEffects === 'none') return true;
|
|
286
|
+
const key = toolApprovalKey(request.capability_id, request.input);
|
|
287
|
+
if (isExecutionApproved({ cwd: state.cwd, key }, { home: state.home })) return true;
|
|
288
|
+
if (!state.interactive) return false;
|
|
289
|
+
|
|
290
|
+
const summary = JSON.stringify(safeToolInput(request.input || {}));
|
|
291
|
+
writeLine(state.write, '');
|
|
292
|
+
writeLine(state.write, ' Agent Sam needs execution permission');
|
|
293
|
+
writeLine(state.write, ` action ${key}`);
|
|
294
|
+
writeLine(state.write, ` target local runtime · ${compactCwd(state.cwd)}`);
|
|
295
|
+
writeLine(state.write, ` effect ${sideEffects}`);
|
|
296
|
+
if (summary && summary !== '{}') writeLine(state.write, ` input ${summary.length > 500 ? `${summary.slice(0, 497)}...` : summary}`);
|
|
297
|
+
writeLine(state.write, ' secrets remain runtime-owned and are not included in the model-visible result.');
|
|
298
|
+
|
|
299
|
+
const choice = await select({
|
|
300
|
+
message: `Allow ${key}?`,
|
|
301
|
+
options: [
|
|
302
|
+
{ value: 'once', label: 'Allow once' },
|
|
303
|
+
{ value: 'always', label: `Always allow ${key} in this project` },
|
|
304
|
+
{ value: 'deny', label: 'Deny' },
|
|
305
|
+
],
|
|
306
|
+
});
|
|
307
|
+
if (isCancel(choice) || choice === 'deny') return false;
|
|
308
|
+
if (choice === 'always') grantExecutionApproval({ cwd: state.cwd, key, label: key }, { home: state.home });
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function runInteractiveModelTurn(prompt, state) {
|
|
313
|
+
const { preferences, model } = selectedModel(state.cwd);
|
|
314
|
+
if (model.provider !== 'openai') throw new Error(`interactive_provider_not_implemented:${model.provider}`);
|
|
315
|
+
const credential = resolveProviderCredential(model.provider, { home: state.home });
|
|
316
|
+
if (!credential.configured) throw new Error(`provider_credential_unavailable:${model.provider}:${credential.error || credential.env || 'not_configured'}`);
|
|
317
|
+
|
|
318
|
+
const activity = createRuntimeActivity({ write: state.write, phase: 'thinking', interactive: state.interactive });
|
|
319
|
+
const provider = createOpenAIResponsesAdapter({ apiKey: credential.value });
|
|
320
|
+
const capabilityAdapter = createCapabilityAdapter();
|
|
321
|
+
const samePolicy = state.session
|
|
322
|
+
&& state.session.model_key === model.model_key
|
|
323
|
+
&& state.session.reasoning_effort === preferences.reasoningEffort
|
|
324
|
+
&& state.session.requested_service_tier === preferences.serviceTier;
|
|
325
|
+
const previousResponseId = samePolicy ? state.session?.provider_state?.previous_response_id : null;
|
|
326
|
+
const previousUsageSnapshot = samePolicy ? state.session?.usage_snapshot : null;
|
|
327
|
+
|
|
328
|
+
activity.start('thinking');
|
|
329
|
+
let result;
|
|
330
|
+
try {
|
|
331
|
+
result = await runResponsesAgent({
|
|
332
|
+
provider,
|
|
333
|
+
capabilityAdapter,
|
|
334
|
+
cwd: state.cwd,
|
|
335
|
+
prompt,
|
|
336
|
+
model: model.model_key,
|
|
337
|
+
reasoningEffort: preferences.reasoningEffort,
|
|
338
|
+
serviceTier: preferences.serviceTier,
|
|
339
|
+
previousResponseId,
|
|
340
|
+
previousUsageSnapshot,
|
|
341
|
+
cumulativeUsage: state.session?.cumulative_usage || null,
|
|
342
|
+
promptCacheKey: state.session?.id || undefined,
|
|
343
|
+
runId: state.session?.id || undefined,
|
|
344
|
+
beforeRequest: (preflight) => approveModelRequest(preflight, state),
|
|
345
|
+
beforeTool: (request) => approveToolExecution(request, state),
|
|
346
|
+
emit(event) {
|
|
347
|
+
if (event?.type === 'usage.snapshot') state.usageSnapshot = event.payload;
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
} catch (error) {
|
|
351
|
+
activity.fail('failed');
|
|
352
|
+
throw error;
|
|
353
|
+
}
|
|
354
|
+
activity.succeed('done');
|
|
355
|
+
if (result.output_text) {
|
|
356
|
+
writeLine(state.write, '');
|
|
357
|
+
writeLine(state.write, result.output_text);
|
|
358
|
+
writeLine(state.write, '');
|
|
359
|
+
}
|
|
360
|
+
state.usageSnapshot = result.usage_snapshot;
|
|
361
|
+
if (state.session) {
|
|
362
|
+
persistSession(state, {
|
|
363
|
+
status: 'active',
|
|
364
|
+
model_key: model.model_key,
|
|
365
|
+
provider_model_id: result.model,
|
|
366
|
+
reasoning_effort: result.reasoning_effort,
|
|
367
|
+
requested_service_tier: result.requested_service_tier,
|
|
368
|
+
actual_service_tier: result.actual_service_tier,
|
|
369
|
+
provider_state: { provider: model.provider, previous_response_id: result.response_id },
|
|
370
|
+
usage_snapshot: result.usage_snapshot,
|
|
371
|
+
cumulative_usage: result.cumulative_usage,
|
|
372
|
+
total_cost_usd: Number(state.session.total_cost_usd || 0) + Number(result.total_cost_usd || 0),
|
|
373
|
+
last_error: null,
|
|
374
|
+
});
|
|
149
375
|
}
|
|
376
|
+
return result;
|
|
150
377
|
}
|
|
151
378
|
|
|
152
379
|
export async function dispatchShellLine(line, state = {}) {
|
|
153
380
|
const tokens = tokenizeShellLine(line);
|
|
154
381
|
const write = state.write || ((text) => process.stdout.write(text));
|
|
382
|
+
state.write = write;
|
|
155
383
|
state.cwd = path.resolve(state.cwd || process.cwd());
|
|
156
384
|
if (!tokens.length) return { handled: true, exit: false, cwd: state.cwd };
|
|
157
|
-
|
|
158
385
|
const [command, ...args] = tokens;
|
|
159
386
|
try {
|
|
160
387
|
switch (command.toLowerCase()) {
|
|
388
|
+
case '/':
|
|
389
|
+
case '/menu':
|
|
390
|
+
await showCommandPicker(state);
|
|
391
|
+
break;
|
|
161
392
|
case '/help':
|
|
162
393
|
write(renderShellCatalog());
|
|
163
|
-
|
|
394
|
+
break;
|
|
164
395
|
case '/exit':
|
|
165
396
|
case '/quit':
|
|
166
397
|
return { handled: true, exit: true, cwd: state.cwd };
|
|
398
|
+
case '/model': {
|
|
399
|
+
const configured = await configureCliPreferences({ cwd: state.cwd, firstRun: false, section: 'model' });
|
|
400
|
+
state.cwd = configured.identity.root;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
case '/reasoning':
|
|
404
|
+
await chooseReasoning(state.cwd, args, state);
|
|
405
|
+
break;
|
|
406
|
+
case '/fast':
|
|
407
|
+
setServiceTier(state.cwd, 'fast', write);
|
|
408
|
+
break;
|
|
409
|
+
case '/flex':
|
|
410
|
+
setServiceTier(state.cwd, 'flex', write);
|
|
411
|
+
break;
|
|
412
|
+
case '/standard':
|
|
413
|
+
setServiceTier(state.cwd, 'default', write);
|
|
414
|
+
break;
|
|
415
|
+
case '/context':
|
|
416
|
+
if (args[0] === 'repo' || args[0] === 'git') await runContext(['--cwd', state.cwd, ...args.slice(1)]);
|
|
417
|
+
else write(renderContextEconomics(buildContextEconomicsReport(state.cwd, {
|
|
418
|
+
activeInputTokens: state.usageSnapshot?.current_context?.input_tokens,
|
|
419
|
+
estimateKind: state.usageSnapshot?.estimate_kind,
|
|
420
|
+
})));
|
|
421
|
+
break;
|
|
167
422
|
case '/status':
|
|
168
423
|
await runStatus(args, { cwd: state.cwd });
|
|
169
424
|
break;
|
|
170
|
-
case '/
|
|
171
|
-
await
|
|
425
|
+
case '/models':
|
|
426
|
+
await runModels(args, { cwd: state.cwd, write, home: state.home });
|
|
172
427
|
break;
|
|
428
|
+
case '/whoami':
|
|
429
|
+
await runWhoami(args, { write, home: state.home });
|
|
430
|
+
break;
|
|
431
|
+
case '/session':
|
|
432
|
+
if (state.session) write(renderSessionReceipt(state.session));
|
|
433
|
+
else writeLine(write, ' No persistent session is active in this shell invocation.');
|
|
434
|
+
break;
|
|
435
|
+
case '/cf':
|
|
436
|
+
case '/cloudflare':
|
|
437
|
+
await runCloudflare(args.length ? args : ['commands'], { cwd: state.cwd, write });
|
|
438
|
+
break;
|
|
439
|
+
case '/settings': {
|
|
440
|
+
const configured = await configureCliPreferences({ cwd: state.cwd, firstRun: false });
|
|
441
|
+
state.cwd = configured.identity.root;
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
173
444
|
case '/pwd':
|
|
174
445
|
writeLine(write, state.cwd);
|
|
175
446
|
break;
|
|
@@ -181,49 +452,59 @@ export async function dispatchShellLine(line, state = {}) {
|
|
|
181
452
|
writeLine(write, state.cwd);
|
|
182
453
|
break;
|
|
183
454
|
}
|
|
184
|
-
case '/git':
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
455
|
+
case '/git':
|
|
456
|
+
spawnGit(state.cwd, args.length ? args : ['status', '--short', '--branch']);
|
|
457
|
+
break;
|
|
458
|
+
case '/diff':
|
|
459
|
+
spawnGit(state.cwd, ['diff', ...args]);
|
|
189
460
|
break;
|
|
190
|
-
}
|
|
191
461
|
case '/db':
|
|
192
462
|
await runDb(args.length ? args : ['status'], { cwd: state.cwd });
|
|
193
463
|
break;
|
|
194
464
|
case '/agent':
|
|
195
|
-
await runLocalAgent(args.join(' '), write);
|
|
465
|
+
await runLocalAgent(args.join(' '), write, { interactive: state.interactive });
|
|
196
466
|
break;
|
|
197
467
|
case '/logs':
|
|
198
468
|
await showLocalLogs(state.cwd, write);
|
|
199
469
|
break;
|
|
200
|
-
case '/tui':
|
|
201
|
-
await withProcessCwd(state.cwd, () => runTui(args));
|
|
202
|
-
break;
|
|
203
470
|
case '/deploy':
|
|
204
471
|
await runDeploy(parseDeployOptions(args, state.cwd));
|
|
205
472
|
break;
|
|
473
|
+
case '/clear':
|
|
474
|
+
write('\x1b[2J\x1b[H');
|
|
475
|
+
break;
|
|
206
476
|
default:
|
|
477
|
+
if (!command.startsWith('/')) {
|
|
478
|
+
await runInteractiveModelTurn(line, state);
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
207
481
|
writeLine(write, ` Unknown Agent Sam command: ${command}`);
|
|
208
|
-
writeLine(write, ' Type /help for available commands.');
|
|
482
|
+
writeLine(write, ' Type / or /help for available commands.');
|
|
209
483
|
return { handled: false, exit: false, cwd: state.cwd };
|
|
210
484
|
}
|
|
211
485
|
} catch (error) {
|
|
212
|
-
|
|
486
|
+
if (state.session) {
|
|
487
|
+
persistSession(state, { last_error: diagnosticFromError(error, { source: 'shell', kind: 'interactive_error' }) });
|
|
488
|
+
}
|
|
489
|
+
if (!error?.reported) {
|
|
490
|
+
for (const line of renderDiagnosticError(error).split('\n')) writeLine(write, ` ${line}`);
|
|
491
|
+
}
|
|
213
492
|
}
|
|
214
|
-
|
|
215
493
|
return { handled: true, exit: false, cwd: state.cwd };
|
|
216
494
|
}
|
|
217
495
|
|
|
218
496
|
export async function runShell(argv = [], options = {}) {
|
|
219
497
|
const write = options.write || ((text) => process.stdout.write(text));
|
|
220
|
-
const state = {
|
|
498
|
+
const state = {
|
|
499
|
+
cwd: path.resolve(options.cwd || process.cwd()),
|
|
500
|
+
write,
|
|
501
|
+
interactive: options.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
502
|
+
usageSnapshot: options.usageSnapshot || options.session?.usage_snapshot || null,
|
|
503
|
+
session: options.session || null,
|
|
504
|
+
home: options.home,
|
|
505
|
+
};
|
|
221
506
|
const sub = argv[0] || '';
|
|
222
|
-
|
|
223
|
-
if (sub === 'list' || sub === 'status') {
|
|
224
|
-
write(renderShellCatalog());
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
507
|
+
if (sub === 'list' || sub === 'status') { write(renderShellCatalog()); return; }
|
|
227
508
|
if (sub === '--command' || sub === '--once') {
|
|
228
509
|
const line = argv.slice(1).join(' ');
|
|
229
510
|
if (!line) throw new Error(`${sub} requires a slash command`);
|
|
@@ -232,22 +513,46 @@ export async function runShell(argv = [], options = {}) {
|
|
|
232
513
|
}
|
|
233
514
|
if (sub) throw new Error(`unknown shell option: ${sub}`);
|
|
234
515
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
516
|
+
if (state.session) {
|
|
517
|
+
state.cwd = path.resolve(state.session.cwd || state.cwd);
|
|
518
|
+
state.session = saveLocalSession({ ...state.session, status: 'active', cwd: state.cwd }, { home: state.home });
|
|
519
|
+
state.usageSnapshot = state.session.usage_snapshot || state.usageSnapshot;
|
|
520
|
+
} else {
|
|
521
|
+
const preferences = readCliPreferences(state.cwd) || {};
|
|
522
|
+
state.session = createLocalSession({
|
|
523
|
+
cwd: state.cwd,
|
|
524
|
+
status: 'active',
|
|
525
|
+
model_key: preferences.modelPreference !== 'auto' ? preferences.modelPreference : null,
|
|
526
|
+
reasoning_effort: preferences.reasoningEffort !== 'auto' ? preferences.reasoningEffort : null,
|
|
527
|
+
requested_service_tier: preferences.serviceTier || 'default',
|
|
528
|
+
}, { home: state.home });
|
|
243
529
|
}
|
|
244
530
|
|
|
531
|
+
if (options.intro !== 'quiet') {
|
|
532
|
+
write(renderShellCatalog());
|
|
533
|
+
writeLine(write, ' Interactive shell ready. Type / for the command picker; /exit to return to your host shell.\n');
|
|
534
|
+
}
|
|
535
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: Boolean(process.stdin.isTTY && process.stdout.isTTY) });
|
|
536
|
+
let interrupted = false;
|
|
537
|
+
rl.on('SIGINT', () => {
|
|
538
|
+
interrupted = true;
|
|
539
|
+
rl.close();
|
|
540
|
+
});
|
|
541
|
+
const promptText = () => {
|
|
542
|
+
if (typeof options.prompt === 'function') return options.prompt(state);
|
|
543
|
+
if (typeof options.prompt === 'string' && options.prompt) return options.prompt;
|
|
544
|
+
return renderShellPrompt(state.cwd);
|
|
545
|
+
};
|
|
546
|
+
if (rl.terminal) { rl.setPrompt(promptText()); rl.prompt(); }
|
|
245
547
|
for await (const line of rl) {
|
|
548
|
+
recordSessionInput(state, line);
|
|
246
549
|
const result = await dispatchShellLine(line, state);
|
|
247
|
-
if (result.exit) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
550
|
+
if (result.exit) { rl.close(); break; }
|
|
551
|
+
if (rl.terminal) { rl.setPrompt(promptText()); rl.prompt(); }
|
|
552
|
+
}
|
|
553
|
+
if (state.session) {
|
|
554
|
+
persistSession(state, { status: interrupted ? 'interrupted' : 'paused', cwd: state.cwd });
|
|
555
|
+
if (options.receipt !== false) write(renderSessionReceipt(state.session));
|
|
252
556
|
}
|
|
557
|
+
return state.session;
|
|
253
558
|
}
|