@visorcraft/idlehands 2.2.4 → 2.2.5
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/agent.js +241 -14
- package/dist/agent.js.map +1 -1
- package/dist/anton/verifier-utils.js +75 -3
- package/dist/anton/verifier-utils.js.map +1 -1
- package/dist/anton/verifier.js +2 -1
- package/dist/anton/verifier.js.map +1 -1
- package/dist/bot/anton-run.js +1 -0
- package/dist/bot/anton-run.js.map +1 -1
- package/dist/bot/basic-commands.js +2 -1
- package/dist/bot/basic-commands.js.map +1 -1
- package/dist/bot/command-logic.js +1 -1
- package/dist/bot/command-logic.js.map +1 -1
- package/dist/bot/commands.js +79 -1
- package/dist/bot/commands.js.map +1 -1
- package/dist/bot/discord-commands.js +121 -18
- package/dist/bot/discord-commands.js.map +1 -1
- package/dist/bot/discord.js +86 -6
- package/dist/bot/discord.js.map +1 -1
- package/dist/bot/runtime-model-picker.js +77 -0
- package/dist/bot/runtime-model-picker.js.map +1 -0
- package/dist/bot/session-settings.js +28 -30
- package/dist/bot/session-settings.js.map +1 -1
- package/dist/bot/telegram-commands.js +161 -36
- package/dist/bot/telegram-commands.js.map +1 -1
- package/dist/bot/telegram.js +6 -1
- package/dist/bot/telegram.js.map +1 -1
- package/dist/bot/ux/events.js.map +1 -1
- package/dist/bot/ux/progress-to-events.js +11 -0
- package/dist/bot/ux/progress-to-events.js.map +1 -1
- package/dist/cli/commands/anton.js +3 -0
- package/dist/cli/commands/anton.js.map +1 -1
- package/dist/cli/commands/editing.js +26 -0
- package/dist/cli/commands/editing.js.map +1 -1
- package/dist/cli/commands/session.js +1 -1
- package/dist/cli/commands/session.js.map +1 -1
- package/dist/config.js +134 -0
- package/dist/config.js.map +1 -1
- package/dist/routing/mode.js +32 -0
- package/dist/routing/mode.js.map +1 -0
- package/dist/routing/turn-router.js +128 -0
- package/dist/routing/turn-router.js.map +1 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -14,6 +14,7 @@ import { buildDefaultSystemPrompt } from './agent/prompt-builder.js';
|
|
|
14
14
|
import { LeakDetector } from './security/leak-detector.js';
|
|
15
15
|
import { PromptGuard } from './security/prompt-guard.js';
|
|
16
16
|
import { ResponseCache } from './agent/response-cache.js';
|
|
17
|
+
import { resilientCall } from './agent/resilient-provider.js';
|
|
17
18
|
import { ToolLoopGuard } from './agent/tool-loop-guard.js';
|
|
18
19
|
import { isLspTool, isMutationTool, isReadOnlyTool, planModeSummary } from './agent/tool-policy.js';
|
|
19
20
|
import { buildToolsSchema } from './agent/tools-schema.js';
|
|
@@ -31,6 +32,7 @@ import { MCPManager } from './mcp.js';
|
|
|
31
32
|
import { BASE_MAX_TOKENS, deriveContextWindow, deriveGenerationParams, supportsVisionModel, } from './model-customization.js';
|
|
32
33
|
import { ReplayStore } from './replay.js';
|
|
33
34
|
import { checkExecSafety, checkPathSafety } from './safety.js';
|
|
35
|
+
import { decideTurnRoute } from './routing/turn-router.js';
|
|
34
36
|
import { normalizeApprovalMode } from './shared/config-utils.js';
|
|
35
37
|
import { collectSnapshot } from './sys/context.js';
|
|
36
38
|
import { ToolError, ValidationError } from './tools/tool-error.js';
|
|
@@ -1078,6 +1080,9 @@ export async function createSession(opts) {
|
|
|
1078
1080
|
let captureEnabled = false;
|
|
1079
1081
|
let capturePath;
|
|
1080
1082
|
let lastCaptureRecord = null;
|
|
1083
|
+
const routedClients = new Map();
|
|
1084
|
+
const probedEndpoints = new Set();
|
|
1085
|
+
const normalizeEndpoint = (endpoint) => endpoint.trim().replace(/\/+$/, '');
|
|
1081
1086
|
const defaultCapturePath = () => {
|
|
1082
1087
|
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1083
1088
|
return path.join(capturesDir, `${stamp}.jsonl`);
|
|
@@ -1086,18 +1091,135 @@ export async function createSession(opts) {
|
|
|
1086
1091
|
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
|
1087
1092
|
await fs.appendFile(outPath, JSON.stringify(record) + '\n', 'utf8');
|
|
1088
1093
|
};
|
|
1089
|
-
const
|
|
1090
|
-
if (typeof
|
|
1094
|
+
const applyClientRuntimeOptions = (target) => {
|
|
1095
|
+
if (typeof target.setVerbose === 'function') {
|
|
1096
|
+
target.setVerbose(cfg.verbose);
|
|
1097
|
+
}
|
|
1098
|
+
if (typeof cfg.response_timeout === 'number' && cfg.response_timeout > 0) {
|
|
1099
|
+
target.setResponseTimeout(cfg.response_timeout);
|
|
1100
|
+
}
|
|
1101
|
+
if (typeof target.setConnectionTimeout === 'function' &&
|
|
1102
|
+
typeof cfg.connection_timeout === 'number' &&
|
|
1103
|
+
cfg.connection_timeout > 0) {
|
|
1104
|
+
target.setConnectionTimeout(cfg.connection_timeout);
|
|
1105
|
+
}
|
|
1106
|
+
if (typeof target.setInitialConnectionCheck === 'function' &&
|
|
1107
|
+
typeof cfg.initial_connection_check === 'boolean') {
|
|
1108
|
+
target.setInitialConnectionCheck(cfg.initial_connection_check);
|
|
1109
|
+
}
|
|
1110
|
+
if (typeof target.setInitialConnectionProbeTimeout === 'function' &&
|
|
1111
|
+
typeof cfg.initial_connection_timeout === 'number' &&
|
|
1112
|
+
cfg.initial_connection_timeout > 0) {
|
|
1113
|
+
target.setInitialConnectionProbeTimeout(cfg.initial_connection_timeout);
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
const attachCaptureHook = (target) => {
|
|
1117
|
+
if (typeof target.setExchangeHook !== 'function')
|
|
1091
1118
|
return;
|
|
1092
|
-
|
|
1119
|
+
target.setExchangeHook(async (record) => {
|
|
1093
1120
|
lastCaptureRecord = record;
|
|
1094
1121
|
if (!captureEnabled)
|
|
1095
1122
|
return;
|
|
1096
|
-
const
|
|
1097
|
-
capturePath =
|
|
1098
|
-
await appendCaptureRecord(record,
|
|
1123
|
+
const outFile = capturePath || defaultCapturePath();
|
|
1124
|
+
capturePath = outFile;
|
|
1125
|
+
await appendCaptureRecord(record, outFile);
|
|
1099
1126
|
});
|
|
1100
1127
|
};
|
|
1128
|
+
const getClientForEndpoint = (endpoint) => {
|
|
1129
|
+
if (!endpoint)
|
|
1130
|
+
return client;
|
|
1131
|
+
const normalized = normalizeEndpoint(endpoint);
|
|
1132
|
+
if (!normalized || normalized === normalizeEndpoint(cfg.endpoint))
|
|
1133
|
+
return client;
|
|
1134
|
+
const existing = routedClients.get(normalized);
|
|
1135
|
+
if (existing)
|
|
1136
|
+
return existing;
|
|
1137
|
+
const routed = new OpenAIClient(normalized, opts.apiKey, cfg.verbose);
|
|
1138
|
+
applyClientRuntimeOptions(routed);
|
|
1139
|
+
attachCaptureHook(routed);
|
|
1140
|
+
routedClients.set(normalized, routed);
|
|
1141
|
+
return routed;
|
|
1142
|
+
};
|
|
1143
|
+
let runtimeRoutingModules = null;
|
|
1144
|
+
let runtimeRoutingUnavailable = false;
|
|
1145
|
+
let runtimeModelIdsCache = null;
|
|
1146
|
+
const loadRuntimeRoutingModules = async () => {
|
|
1147
|
+
if (runtimeRoutingUnavailable)
|
|
1148
|
+
return null;
|
|
1149
|
+
if (runtimeRoutingModules)
|
|
1150
|
+
return runtimeRoutingModules;
|
|
1151
|
+
try {
|
|
1152
|
+
const [planner, executor, store] = await Promise.all([
|
|
1153
|
+
import('./runtime/planner.js'),
|
|
1154
|
+
import('./runtime/executor.js'),
|
|
1155
|
+
import('./runtime/store.js'),
|
|
1156
|
+
]);
|
|
1157
|
+
runtimeRoutingModules = { planner, executor, store };
|
|
1158
|
+
return runtimeRoutingModules;
|
|
1159
|
+
}
|
|
1160
|
+
catch {
|
|
1161
|
+
runtimeRoutingUnavailable = true;
|
|
1162
|
+
return null;
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
const loadRuntimeModelIds = async () => {
|
|
1166
|
+
if (runtimeModelIdsCache)
|
|
1167
|
+
return runtimeModelIdsCache;
|
|
1168
|
+
const mods = await loadRuntimeRoutingModules();
|
|
1169
|
+
if (!mods) {
|
|
1170
|
+
runtimeModelIdsCache = new Set();
|
|
1171
|
+
return runtimeModelIdsCache;
|
|
1172
|
+
}
|
|
1173
|
+
try {
|
|
1174
|
+
const runtimes = await mods.store.loadRuntimes();
|
|
1175
|
+
runtimeModelIdsCache = new Set(runtimes.models.filter((m) => m.enabled !== false).map((m) => m.id));
|
|
1176
|
+
return runtimeModelIdsCache;
|
|
1177
|
+
}
|
|
1178
|
+
catch {
|
|
1179
|
+
runtimeModelIdsCache = new Set();
|
|
1180
|
+
return runtimeModelIdsCache;
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
const ensureRuntimeModelActive = async (runtimeModelId) => {
|
|
1184
|
+
const mods = await loadRuntimeRoutingModules();
|
|
1185
|
+
if (!mods)
|
|
1186
|
+
throw new Error('Runtime routing is unavailable in this build/environment');
|
|
1187
|
+
const runtimes = await mods.store.loadRuntimes();
|
|
1188
|
+
runtimeModelIdsCache = new Set(runtimes.models.filter((m) => m.enabled !== false).map((m) => m.id));
|
|
1189
|
+
const modelExists = runtimes.models.some((m) => m.enabled !== false && m.id === runtimeModelId);
|
|
1190
|
+
if (!modelExists) {
|
|
1191
|
+
throw new Error(`Runtime model not found or disabled: ${runtimeModelId}`);
|
|
1192
|
+
}
|
|
1193
|
+
let active = await mods.executor.loadActiveRuntime();
|
|
1194
|
+
if (active?.healthy && active.modelId === runtimeModelId && active.endpoint) {
|
|
1195
|
+
if (normalizeEndpoint(active.endpoint) !== normalizeEndpoint(cfg.endpoint)) {
|
|
1196
|
+
await setEndpoint(active.endpoint);
|
|
1197
|
+
}
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const planResult = mods.planner.plan({ modelId: runtimeModelId, mode: 'live' }, runtimes, active);
|
|
1201
|
+
if (!planResult.ok) {
|
|
1202
|
+
throw new Error(`Runtime switch plan failed for ${runtimeModelId}: ${planResult.reason}`);
|
|
1203
|
+
}
|
|
1204
|
+
if (!planResult.reuse) {
|
|
1205
|
+
const execResult = await mods.executor.execute(planResult, {
|
|
1206
|
+
confirm: async () => false,
|
|
1207
|
+
});
|
|
1208
|
+
if (!execResult.ok) {
|
|
1209
|
+
throw new Error(`Runtime switch failed for ${runtimeModelId}: ${execResult.error ?? 'unknown error'}`);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
active = await mods.executor.loadActiveRuntime();
|
|
1213
|
+
if (!active?.endpoint || active.healthy !== true) {
|
|
1214
|
+
throw new Error(`Runtime did not become healthy for ${runtimeModelId}`);
|
|
1215
|
+
}
|
|
1216
|
+
if (normalizeEndpoint(active.endpoint) !== normalizeEndpoint(cfg.endpoint)) {
|
|
1217
|
+
await setEndpoint(active.endpoint);
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
const wireCaptureHook = () => {
|
|
1221
|
+
attachCaptureHook(client);
|
|
1222
|
+
};
|
|
1101
1223
|
wireCaptureHook();
|
|
1102
1224
|
const replayEnabled = cfg.trifecta?.enabled !== false && cfg.trifecta?.replay?.enabled !== false;
|
|
1103
1225
|
const replay = replayEnabled ? (opts.runtime?.replay ?? new ReplayStore()) : undefined;
|
|
@@ -1228,9 +1350,9 @@ export async function createSession(opts) {
|
|
|
1228
1350
|
else {
|
|
1229
1351
|
client = new OpenAIClient(normalized, opts.apiKey, cfg.verbose);
|
|
1230
1352
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1353
|
+
applyClientRuntimeOptions(client);
|
|
1354
|
+
routedClients.clear();
|
|
1355
|
+
probedEndpoints.clear();
|
|
1234
1356
|
wireCaptureHook();
|
|
1235
1357
|
modelsList = normalizeModelsResponse(await client.models());
|
|
1236
1358
|
const chosen = modelName?.trim()
|
|
@@ -1288,6 +1410,7 @@ export async function createSession(opts) {
|
|
|
1288
1410
|
const close = async () => {
|
|
1289
1411
|
await mcpManager?.close().catch(() => { });
|
|
1290
1412
|
await lspManager?.close().catch(() => { });
|
|
1413
|
+
routedClients.clear();
|
|
1291
1414
|
vault?.close();
|
|
1292
1415
|
lens?.close();
|
|
1293
1416
|
};
|
|
@@ -1575,6 +1698,9 @@ export async function createSession(opts) {
|
|
|
1575
1698
|
if (typeof client.probeConnection === 'function') {
|
|
1576
1699
|
await client.probeConnection();
|
|
1577
1700
|
initialConnectionProbeDone = true;
|
|
1701
|
+
if (typeof client.getEndpoint === 'function') {
|
|
1702
|
+
probedEndpoints.add(normalizeEndpoint(client.getEndpoint()));
|
|
1703
|
+
}
|
|
1578
1704
|
}
|
|
1579
1705
|
}
|
|
1580
1706
|
if (retrievalRequested) {
|
|
@@ -1621,6 +1747,38 @@ export async function createSession(opts) {
|
|
|
1621
1747
|
});
|
|
1622
1748
|
return await finalizeAsk(miss);
|
|
1623
1749
|
}
|
|
1750
|
+
const turnRoute = decideTurnRoute(cfg, rawInstructionText, model);
|
|
1751
|
+
const primaryRoute = turnRoute.providerTargets[0];
|
|
1752
|
+
const runtimeModelIds = await loadRuntimeModelIds();
|
|
1753
|
+
const routeRuntimeFallbackModels = (primaryRoute?.fallbackModels ?? []).filter((m) => runtimeModelIds.has(m));
|
|
1754
|
+
const routeApiFallbackModels = (primaryRoute?.fallbackModels ?? []).filter((m) => !runtimeModelIds.has(m));
|
|
1755
|
+
const primaryUsesRuntimeModel = !!primaryRoute?.model && runtimeModelIds.has(primaryRoute.model);
|
|
1756
|
+
// Non-runtime route models can be selected directly in-session.
|
|
1757
|
+
if (!primaryUsesRuntimeModel && primaryRoute?.model && primaryRoute.model !== model) {
|
|
1758
|
+
setModel(primaryRoute.model);
|
|
1759
|
+
}
|
|
1760
|
+
if (cfg.verbose) {
|
|
1761
|
+
const routeParts = [
|
|
1762
|
+
`requested=${turnRoute.requestedMode}`,
|
|
1763
|
+
`selected=${turnRoute.selectedMode}`,
|
|
1764
|
+
`source=${turnRoute.selectedModeSource}`,
|
|
1765
|
+
`hint=${turnRoute.classificationHint ?? 'none'}`,
|
|
1766
|
+
`provider=${primaryRoute?.name ?? 'default'}`,
|
|
1767
|
+
`model=${primaryRoute?.model ?? model}`,
|
|
1768
|
+
];
|
|
1769
|
+
if (turnRoute.heuristicDecision)
|
|
1770
|
+
routeParts.push(`heuristic=${turnRoute.heuristicDecision}`);
|
|
1771
|
+
if (primaryUsesRuntimeModel) {
|
|
1772
|
+
const runtimeChain = [primaryRoute?.model, ...routeRuntimeFallbackModels]
|
|
1773
|
+
.filter(Boolean)
|
|
1774
|
+
.join(' -> ');
|
|
1775
|
+
routeParts.push(`runtime_chain=${runtimeChain || 'none'}`);
|
|
1776
|
+
}
|
|
1777
|
+
else if (routeApiFallbackModels.length) {
|
|
1778
|
+
routeParts.push(`api_fallbacks=${routeApiFallbackModels.join(',')}`);
|
|
1779
|
+
}
|
|
1780
|
+
console.error(`[routing] ${routeParts.join(' ')}`);
|
|
1781
|
+
}
|
|
1624
1782
|
const persistReviewArtifact = async (finalText) => {
|
|
1625
1783
|
if (!vault || !shouldPersistReviewArtifact)
|
|
1626
1784
|
return;
|
|
@@ -2045,8 +2203,7 @@ export async function createSession(opts) {
|
|
|
2045
2203
|
}
|
|
2046
2204
|
}
|
|
2047
2205
|
if (!resp) {
|
|
2048
|
-
|
|
2049
|
-
model,
|
|
2206
|
+
const chatOptsBase = {
|
|
2050
2207
|
messages,
|
|
2051
2208
|
tools: toolsForTurn,
|
|
2052
2209
|
tool_choice: toolChoiceForTurn,
|
|
@@ -2055,9 +2212,10 @@ export async function createSession(opts) {
|
|
|
2055
2212
|
max_tokens: maxTokens,
|
|
2056
2213
|
extra: {
|
|
2057
2214
|
cache_prompt: cfg.cache_prompt ?? true,
|
|
2058
|
-
// Speculative decoding: draft model params for llama-server
|
|
2059
2215
|
...(cfg.draft_model ? { draft_model: cfg.draft_model } : {}),
|
|
2060
|
-
...(cfg.draft_n
|
|
2216
|
+
...(cfg.draft_n
|
|
2217
|
+
? { speculative: { n: cfg.draft_n, p_min: cfg.draft_p_min ?? 0.5 } }
|
|
2218
|
+
: {}),
|
|
2061
2219
|
...(frequencyPenalty && { frequency_penalty: frequencyPenalty }),
|
|
2062
2220
|
...(presencePenalty && { presence_penalty: presencePenalty }),
|
|
2063
2221
|
},
|
|
@@ -2065,7 +2223,73 @@ export async function createSession(opts) {
|
|
|
2065
2223
|
requestId: `r${reqCounter}`,
|
|
2066
2224
|
onToken: hookObj.onToken,
|
|
2067
2225
|
onFirstDelta,
|
|
2068
|
-
}
|
|
2226
|
+
};
|
|
2227
|
+
if (primaryUsesRuntimeModel && primaryRoute?.model) {
|
|
2228
|
+
// Runtime-native routing: lane model/fallbacks reference runtime model IDs.
|
|
2229
|
+
const runtimePrimaryModel = primaryRoute.model;
|
|
2230
|
+
const runtimeFallbackMap = {};
|
|
2231
|
+
if (routeRuntimeFallbackModels.length > 0) {
|
|
2232
|
+
runtimeFallbackMap[runtimePrimaryModel] = routeRuntimeFallbackModels;
|
|
2233
|
+
}
|
|
2234
|
+
resp = await resilientCall([
|
|
2235
|
+
{
|
|
2236
|
+
name: 'runtime-router',
|
|
2237
|
+
execute: async (runtimeModelId) => {
|
|
2238
|
+
await ensureRuntimeModelActive(runtimeModelId);
|
|
2239
|
+
const runtimeClient = getClientForEndpoint();
|
|
2240
|
+
const runtimeModel = model;
|
|
2241
|
+
return runtimeClient.chatStream({ ...chatOptsBase, model: runtimeModel });
|
|
2242
|
+
},
|
|
2243
|
+
},
|
|
2244
|
+
], runtimePrimaryModel, {
|
|
2245
|
+
maxRetries: 0,
|
|
2246
|
+
modelFallbacks: runtimeFallbackMap,
|
|
2247
|
+
onRetry: (info) => {
|
|
2248
|
+
if (cfg.verbose) {
|
|
2249
|
+
console.error(`[routing] runtime-fallback: model=${info.model} attempt=${info.attempt}/${info.maxAttempts} reason=${info.reason}`);
|
|
2250
|
+
}
|
|
2251
|
+
},
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
else {
|
|
2255
|
+
const routeEndpoint = primaryRoute?.endpoint;
|
|
2256
|
+
const activeClient = getClientForEndpoint(routeEndpoint);
|
|
2257
|
+
const endpointKey = routeEndpoint ? normalizeEndpoint(routeEndpoint) : undefined;
|
|
2258
|
+
if (endpointKey && !probedEndpoints.has(endpointKey)) {
|
|
2259
|
+
if (typeof activeClient.probeConnection === 'function') {
|
|
2260
|
+
try {
|
|
2261
|
+
await activeClient.probeConnection();
|
|
2262
|
+
}
|
|
2263
|
+
catch {
|
|
2264
|
+
// best-effort: if probe fails we still try the call
|
|
2265
|
+
}
|
|
2266
|
+
probedEndpoints.add(endpointKey);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
const routeModel = primaryRoute?.model ?? model;
|
|
2270
|
+
if (routeApiFallbackModels.length > 0) {
|
|
2271
|
+
const modelFallbackMap = {
|
|
2272
|
+
[routeModel]: routeApiFallbackModels,
|
|
2273
|
+
};
|
|
2274
|
+
resp = await resilientCall([
|
|
2275
|
+
{
|
|
2276
|
+
name: primaryRoute?.name ?? 'default',
|
|
2277
|
+
execute: (m) => activeClient.chatStream({ ...chatOptsBase, model: m }),
|
|
2278
|
+
},
|
|
2279
|
+
], routeModel, {
|
|
2280
|
+
maxRetries: 1,
|
|
2281
|
+
modelFallbacks: modelFallbackMap,
|
|
2282
|
+
onRetry: (info) => {
|
|
2283
|
+
if (cfg.verbose) {
|
|
2284
|
+
console.error(`[routing] retry: provider=${info.provider} model=${info.model} attempt=${info.attempt}/${info.maxAttempts} reason=${info.reason}`);
|
|
2285
|
+
}
|
|
2286
|
+
},
|
|
2287
|
+
});
|
|
2288
|
+
}
|
|
2289
|
+
else {
|
|
2290
|
+
resp = await activeClient.chatStream({ ...chatOptsBase, model: routeModel });
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2069
2293
|
} // end if (!resp) — cache miss path
|
|
2070
2294
|
// Successful response resets overflow recovery budget.
|
|
2071
2295
|
overflowCompactionAttempts = 0;
|
|
@@ -3533,6 +3757,9 @@ export async function createSession(opts) {
|
|
|
3533
3757
|
refreshServerHealth,
|
|
3534
3758
|
getPerfSummary,
|
|
3535
3759
|
getToolLoopStats: () => lastToolLoopStats,
|
|
3760
|
+
get lastAskInstructionText() {
|
|
3761
|
+
return lastAskInstructionText;
|
|
3762
|
+
},
|
|
3536
3763
|
captureOn,
|
|
3537
3764
|
captureOff,
|
|
3538
3765
|
captureLast,
|