@buildaharness/aielia 0.3.3 → 0.3.4
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-loop.d.ts +9 -1
- package/dist/assistant-session.d.ts +4 -4
- package/dist/assistant-types.d.ts +8 -0
- package/dist/assistant.d.ts +39 -0
- package/dist/{bin-Btzhiyzi.js → bin-CdVTAe7j.js} +135 -34
- package/dist/bin-CdVTAe7j.js.map +1 -0
- package/dist/cli-session.d.ts +12 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +1 -1
- package/dist/config.d.ts +22 -0
- package/dist/first-run.d.ts +3 -0
- package/dist/goal-graph-flag.d.ts +44 -0
- package/dist/goal-graph-reconcile.d.ts +55 -0
- package/dist/goal-graph-service.d.ts +48 -0
- package/dist/goal-graph-store.d.ts +485 -0
- package/dist/goal-graph-suggest-flag.d.ts +34 -0
- package/dist/goal-identity-matcher.d.ts +21 -0
- package/dist/goal-thread-identity.d.ts +32 -0
- package/dist/goal-thread-scheduler.d.ts +78 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +86 -71
- package/dist/live-steering-channel.d.ts +31 -0
- package/dist/next-step-proposer.d.ts +52 -0
- package/dist/plan-approval-service.d.ts +1 -1
- package/dist/plan-drafting-service.d.ts +1 -1
- package/dist/{cli-session-DnSc6Hhx.js → provider-setup-CPbB5y_V.js} +4788 -323
- package/dist/provider-setup-CPbB5y_V.js.map +1 -0
- package/dist/provider-setup.d.ts +51 -0
- package/dist/response-service.d.ts +18 -2
- package/dist/scope-urgency-classifier.d.ts +29 -0
- package/dist/supervisor-flag.d.ts +20 -0
- package/dist/{tui-app-CFfOP2DC.js → tui-app-DeyZ4Pr9.js} +3 -3
- package/dist/{tui-app-CFfOP2DC.js.map → tui-app-DeyZ4Pr9.js.map} +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
- package/dist/bin-Btzhiyzi.js.map +0 -1
- package/dist/cli-session-DnSc6Hhx.js.map +0 -1
package/dist/agent-loop.d.ts
CHANGED
|
@@ -196,6 +196,14 @@ export declare class AgentLoop {
|
|
|
196
196
|
onToolStep?: (step: AssistantToolStep) => void;
|
|
197
197
|
onUsage?: (usage: TokenUsage) => void;
|
|
198
198
|
riskHint?: TurnIntentClassification['riskLevel'];
|
|
199
|
+
/**
|
|
200
|
+
* Mid-turn steering (goalGraphMode): messages the user sent while this turn runs that the
|
|
201
|
+
* scope×urgency classifier judged apply to the answer being produced right now. Called once
|
|
202
|
+
* per iteration, immediately before the LLM call; anything returned is spliced in as a user
|
|
203
|
+
* turn so the model itself applies it — no lexical matching, unlike the harness's
|
|
204
|
+
* caller-constraint output contract.
|
|
205
|
+
*/
|
|
206
|
+
takeSteeringNotes?: () => string[];
|
|
199
207
|
}): (toolCtx: ToolExecutorContext) => Promise<unknown>;
|
|
200
208
|
/**
|
|
201
209
|
* R3 of the internal plan: the wiring-level counterpart to
|
|
@@ -208,7 +216,7 @@ export declare class AgentLoop {
|
|
|
208
216
|
* back whatever got pushed to it once the harness run using this proposer has finished (mirrors
|
|
209
217
|
* `ToolLoopResult`'s `sources` field on the flag-OFF path).
|
|
210
218
|
*/
|
|
211
|
-
createOneLoopProposer(sessionId: string, transcript: ChatMessage[], userMessage: string, systemPrompt: string, onToken?: (token: string) => void, onToolStep?: (step: AssistantToolStep) => void, onUsage?: (usage: TokenUsage) => void, riskHint?: TurnIntentClassification['riskLevel']): {
|
|
219
|
+
createOneLoopProposer(sessionId: string, transcript: ChatMessage[], userMessage: string, systemPrompt: string, onToken?: (token: string) => void, onToolStep?: (step: AssistantToolStep) => void, onUsage?: (usage: TokenUsage) => void, riskHint?: TurnIntentClassification['riskLevel'], takeSteeringNotes?: () => string[]): {
|
|
212
220
|
proposer: (toolCtx: ToolExecutorContext) => Promise<unknown>;
|
|
213
221
|
sources: AssistantSource[];
|
|
214
222
|
};
|
|
@@ -99,8 +99,8 @@ export declare class AssistantSession {
|
|
|
99
99
|
* isn't set.
|
|
100
100
|
*/
|
|
101
101
|
recordSpend(sessionId: string, usage: TokenUsage | undefined): Promise<void>;
|
|
102
|
-
/** Read-only — null means plan mode has never been entered (or was already exited/cancelled) for this session. */
|
|
103
|
-
getPlanModeState(sessionId: string): Promise<PlanModeState | null>;
|
|
102
|
+
/** Read-only — null means plan mode has never been entered (or was already exited/cancelled) for this session (or this `threadId`, once Phase 5's per-thread rescoping applies — see `planModeKey`). */
|
|
103
|
+
getPlanModeState(sessionId: string, threadId?: string): Promise<PlanModeState | null>;
|
|
104
104
|
/**
|
|
105
105
|
* The one place `planMode.active` ever flips true — a fresh `draftId` per entry, even if a
|
|
106
106
|
* prior drafting session was cancelled without ever reaching approval, so a stale draftId can
|
|
@@ -108,9 +108,9 @@ export declare class AssistantSession {
|
|
|
108
108
|
* a new state, same as a fresh entry) — callers that only ever call this once per drafting
|
|
109
109
|
* session (P3's future auto-trigger) never observe that.
|
|
110
110
|
*/
|
|
111
|
-
enterPlanMode(sessionId: string): Promise<PlanModeState>;
|
|
111
|
+
enterPlanMode(sessionId: string, threadId?: string): Promise<PlanModeState>;
|
|
112
112
|
/** Clears `planMode.active` — the only two designed callers are an explicit cancel phrase (PlanDraftingService) and P2's future approval resolution. */
|
|
113
|
-
exitPlanMode(sessionId: string): Promise<void>;
|
|
113
|
+
exitPlanMode(sessionId: string, threadId?: string): Promise<void>;
|
|
114
114
|
/** The session's conversation transcript, oldest first — same array `turn()` reads/appends to. Used by `/export`. */
|
|
115
115
|
getTranscript(sessionId: string): Promise<ChatMessage[]>;
|
|
116
116
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NextStepSuggestion } from './next-step-proposer.js';
|
|
1
2
|
import { RiskState, LayerActivityEvent, AskQuestion } from '@buildaharness/harness';
|
|
2
3
|
import { TokenUsage } from '@buildaharness/runtime';
|
|
3
4
|
import { AnswerClaim } from './answer-claim.js';
|
|
@@ -63,6 +64,13 @@ export interface AssistantTurnResult {
|
|
|
63
64
|
*/
|
|
64
65
|
status: 'ok' | 'needs_approval' | 'escalated' | 'needs_clarification' | 'needs_plan_approval';
|
|
65
66
|
reply: string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Up to three concrete next steps proposed after a full (non-trivial, `ok`) turn, most confident
|
|
69
|
+
* first — for the front end to show under the reply. Advisory only: never executed, never
|
|
70
|
+
* persisted from here. Present only when the caller enabled `goalGraphSuggestMode`
|
|
71
|
+
* (PersonalAssistantOptions) and the proposer returned at least one; absent otherwise.
|
|
72
|
+
*/
|
|
73
|
+
nextSteps?: NextStepSuggestion[];
|
|
66
74
|
reason?: string;
|
|
67
75
|
/** See ProposerKind. Always set (defaults to 'posthoc'), on every status. */
|
|
68
76
|
proposerKind?: ProposerKind;
|
package/dist/assistant.d.ts
CHANGED
|
@@ -16,11 +16,15 @@ import { PlanRecord } from './plan-store.js';
|
|
|
16
16
|
import { OneLoopMode } from './one-loop-flag.js';
|
|
17
17
|
import { AskMode } from './ask-mode-flag.js';
|
|
18
18
|
import { PlanRolloutMode } from './plan-mode-flag.js';
|
|
19
|
+
import { GoalGraphSuggestMode } from './goal-graph-suggest-flag.js';
|
|
20
|
+
import { GoalGraphState } from './goal-graph-service.js';
|
|
21
|
+
import { LiveSteeringChannel } from './live-steering-channel.js';
|
|
19
22
|
import { DebugLogEntry } from './debug-log.js';
|
|
20
23
|
import { AssistantTurnResult, AssistantProgress } from './assistant-types.js';
|
|
21
24
|
export type { MemorySummary, MemoryExport, PendingFact } from './memory-service.js';
|
|
22
25
|
export type { FactCategory } from './turn-intent-classifier.js';
|
|
23
26
|
export type { IndexedMessage, TranscriptSearchHit } from './assistant-session.js';
|
|
27
|
+
export type { GoalGraphState, GoalThreadView, GoalThreadVisibility, GoalTaskSummary } from './goal-graph-service.js';
|
|
24
28
|
export type { BatchBudgetState } from './agent-loop.js';
|
|
25
29
|
export { trimmedAverage, nextItemBudget } from './agent-loop.js';
|
|
26
30
|
export type { AssistantSource } from './assistant-source.js';
|
|
@@ -105,6 +109,21 @@ export interface TurnOptions {
|
|
|
105
109
|
seedFailures: number;
|
|
106
110
|
onInjected?: () => void;
|
|
107
111
|
};
|
|
112
|
+
/**
|
|
113
|
+
* Phase 4 of plans/hierarchical_goal_tree_and_steering_plan.html (R1/R4) — the session-scoped
|
|
114
|
+
* LiveSteeringChannel a caller (cli.ts/App.tsx, Phase 3) built for mid-task steering. When
|
|
115
|
+
* present, `turn()` wraps it in a scope×urgency-classifying UpdateChannel
|
|
116
|
+
* (goal-graph-reconcile.ts) and threads it into harnessBridge.run() so queued messages are
|
|
117
|
+
* absorbed at the harness's own checkCallerUpdates iteration boundary instead of waiting for
|
|
118
|
+
* the turn to finish. PersonalAssistant never reads goalGraphMode itself (same convention as
|
|
119
|
+
* oneLoopMode/askMode/planMode) — the caller decides whether to pass this at all; when absent,
|
|
120
|
+
* behavior is exactly today's (INV-43). Whatever this turn's harness run doesn't get around to
|
|
121
|
+
* classifying (it ends before the queue drains, or never calls harnessBridge.run() at all — a
|
|
122
|
+
* trivial turn) is pushed back onto this same channel in a `finally`, so the caller's own
|
|
123
|
+
* post-turn fallback drain (built in Phase 3) still picks it up — R4's "never silently drop"
|
|
124
|
+
* holds either way.
|
|
125
|
+
*/
|
|
126
|
+
steeringChannel?: LiveSteeringChannel;
|
|
108
127
|
}
|
|
109
128
|
export interface PersonalAssistantOptions {
|
|
110
129
|
llmClient: ILLMClient;
|
|
@@ -207,6 +226,16 @@ export interface PersonalAssistantOptions {
|
|
|
207
226
|
* call resolveOneLoopMode(process.env) and pass the result here.
|
|
208
227
|
*/
|
|
209
228
|
oneLoopMode?: OneLoopMode;
|
|
229
|
+
/**
|
|
230
|
+
* R7 (Tier 1.5) — next-step suggestions, one bounded LLM call each: after every full
|
|
231
|
+
* (non-trivial, `ok`) turn, up to three options are attached to the result as `nextSteps` for
|
|
232
|
+
* the front end to show under the reply; and when a GoalThread finishes its last task, its own
|
|
233
|
+
* suggestions are persisted on the thread for `/goals`. Defaults to 'disabled' — like
|
|
234
|
+
* oneLoopMode/askMode/planMode, the front end resolves the flag (cli.ts / App.tsx pass
|
|
235
|
+
* `config.goalGraphSuggestMode`, which defaults to enabled), so a library or benchmark caller
|
|
236
|
+
* that never asks for suggestions makes no extra call and sees no new field (INV-43).
|
|
237
|
+
*/
|
|
238
|
+
goalGraphSuggestMode?: GoalGraphSuggestMode;
|
|
210
239
|
/**
|
|
211
240
|
* Q2 of the internal plan — global-flag control point (tier 1 of
|
|
212
241
|
* INV-29) for the ask-question mechanism. Undefined (the default) falls back to
|
|
@@ -269,6 +298,8 @@ export declare class PersonalAssistant {
|
|
|
269
298
|
* the internal plan phase B1.
|
|
270
299
|
*/
|
|
271
300
|
private lastProposerKind;
|
|
301
|
+
/** See PersonalAssistantOptions.goalGraphSuggestMode. */
|
|
302
|
+
private readonly goalGraphSuggestMode;
|
|
272
303
|
private readonly memoryService;
|
|
273
304
|
private readonly session;
|
|
274
305
|
private readonly agentLoop;
|
|
@@ -330,6 +361,14 @@ export declare class PersonalAssistant {
|
|
|
330
361
|
* all). Returns `null` when no plan record exists for this session yet.
|
|
331
362
|
*/
|
|
332
363
|
getPlanState(sessionId: string): Promise<PlanRecord | null>;
|
|
364
|
+
/**
|
|
365
|
+
* Phase 7 of plans/hierarchical_goal_tree_and_steering_plan.html (R5, "Visibility") — the shared
|
|
366
|
+
* read surface behind the CLI's `/goals` and chat-ui's GoalsPanel, analogous to `searchTranscript`
|
|
367
|
+
* above. A pure query over whatever Phases 4-5 already populate in this session's
|
|
368
|
+
* `GoalGraphRecord`; never mints or mutates one. Returns the empty state for a session with no
|
|
369
|
+
* goal graph yet, same "nothing to show" convention `getGoalGraphState` itself documents.
|
|
370
|
+
*/
|
|
371
|
+
getGoalGraphState(sessionId: string): Promise<GoalGraphState>;
|
|
333
372
|
/**
|
|
334
373
|
* P9 of the internal plan — the lightweight plan-sketch delegate: a
|
|
335
374
|
* cheaper "just go research and draft an approach" path than plan mode's durable task-graph
|
|
@@ -8,7 +8,7 @@ import { homedir, tmpdir } from "node:os";
|
|
|
8
8
|
import { join, dirname, resolve } from "node:path";
|
|
9
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
10
|
import { OPENROUTER_DEFAULT_MODEL, OPENAI_DEFAULT_MODEL, ANTHROPIC_DEFAULT_MODEL, InMemoryReminderStore, FileSystemAdapter, FileSystemExperienceStore, LLMClient, OpenAICompatibleLLMClient, OPENROUTER_EXTRA_HEADERS, OPENROUTER_BASE_URL, OPENAI_BASE_URL, AnthropicLLMClient } from "@buildaharness/runtime";
|
|
11
|
-
import {
|
|
11
|
+
import { G as buildClaudePrompt, aT as stripJsonCodeFence, a as ALREADY_STAGED_ACTION_TOOL, aS as stagedActionInput, aE as parseClaudeCliOutput, aU as stripMcpToolPrefix, P as PROVIDER_SETUP, Q as cleanApiKey, J as checkApiKeyFormat, a0 as envOverridesFromProcessEnv, aJ as resolveConfig, aQ as shouldLaunchTuiApp, aW as testApiKey, aY as validateConfig, c as ConfigValidationError, ai as formatSpendCapStatus, r as PersonalAssistant, ap as isGoalGraphSuggestEnabled, ao as isGoalGraphEnabled, B as braveSearch, X as createSmtpSender, V as createResendSender, p as LiveSteeringChannel, af as formatNextSteps, K as classifyError, ay as nodeToLayer, L as LAYER_DISPLAY_NAME, ax as nodeDisplayName, a1 as estimateCostUsd, a6 as formatConfigListing, an as isConfigKey, C as CONFIG_KEYS, E as ENV_VAR_FOR_CONFIG_KEY, aF as parseConfigValue, d as ConfigValueParseError, a8 as formatDoctorReport, a7 as formatCostSummary, aa as formatGoalGraphState, ah as formatSearchResults, al as formatUndoLogListing, Y as defaultExportFilename, ak as formatTranscriptMarkdown, aj as formatStatus, ab as formatHelp, m as LAYER_ORDER, H as buildWhyChain, n as LAYER_SHORT_CODE, Z as defaultMemoryExportFilename, ac as formatMemoryExport, ad as formatMemoryPendingOutcome, ae as formatMemorySummary } from "./provider-setup-CPbB5y_V.js";
|
|
12
12
|
import { spawn } from "node:child_process";
|
|
13
13
|
import { createServer, connect } from "node:net";
|
|
14
14
|
import { createRequire } from "node:module";
|
|
@@ -66,7 +66,7 @@ function createNodeFsBackend() {
|
|
|
66
66
|
}
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
const CLI_VERSION = "0.3.
|
|
69
|
+
const CLI_VERSION = "0.3.4";
|
|
70
70
|
function seaCacheDir(version, home = homedir()) {
|
|
71
71
|
return join(home, ".buildaharness", "personal-assistant", "sea-cache", version);
|
|
72
72
|
}
|
|
@@ -650,6 +650,7 @@ function resolveNonInteractiveApprovalMode(env) {
|
|
|
650
650
|
console.error(`[warning] ASSISTANT_NON_INTERACTIVE_APPROVAL="${raw}" is not "decline" or "require-tty" — ignoring.`);
|
|
651
651
|
return void 0;
|
|
652
652
|
}
|
|
653
|
+
const MAX_KEY_ATTEMPTS = 3;
|
|
653
654
|
function alreadyConfigured(deps) {
|
|
654
655
|
const { persisted, overriddenKeys } = deps;
|
|
655
656
|
if (persisted.llmBackend !== void 0) return true;
|
|
@@ -659,50 +660,94 @@ function alreadyConfigured(deps) {
|
|
|
659
660
|
}
|
|
660
661
|
return false;
|
|
661
662
|
}
|
|
662
|
-
const PROVIDER_CHOICES = {
|
|
663
|
-
"1": { backend: "anthropic", label: "Anthropic (Claude) — needs an sk-ant-… key" },
|
|
664
|
-
"2": { backend: "openai", label: "OpenAI — needs an sk-… key" },
|
|
665
|
-
"3": { backend: "openrouter", label: "OpenRouter — needs an sk-or-… key" }
|
|
666
|
-
};
|
|
667
663
|
async function maybeRunFirstRunSetup(deps) {
|
|
668
|
-
const { configStore, persisted, isInteractive, ask, detectClaudeCli, log } = deps;
|
|
664
|
+
const { configStore, persisted, isInteractive, ask, detectClaudeCli, log, testKey } = deps;
|
|
669
665
|
if (alreadyConfigured(deps)) return persisted;
|
|
670
666
|
if (!isInteractive) return persisted;
|
|
671
667
|
log("");
|
|
672
|
-
log("Welcome to Aielia
|
|
673
|
-
log("
|
|
668
|
+
log("Welcome to Aielia! 👋 Let’s get you set up — it takes about a minute.");
|
|
669
|
+
log("");
|
|
670
|
+
log("Aielia needs an AI model to think with. Pick where that comes from below.");
|
|
671
|
+
log("(You can change this any time with /config, or press Ctrl+C to skip.)");
|
|
674
672
|
log("");
|
|
675
673
|
const patch = {};
|
|
676
674
|
if (await detectClaudeCli()) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
);
|
|
675
|
+
log("✓ Claude is already running on this computer (Claude Code is installed and signed in).");
|
|
676
|
+
log(" That means you can start right now — no API key, no extra account, nothing to paste.");
|
|
677
|
+
log("");
|
|
678
|
+
const useClaude = await ask("Use your Claude login? (Y/n — Enter means yes) ");
|
|
680
679
|
if (useClaude === "" || useClaude.toLowerCase().startsWith("y")) {
|
|
681
680
|
patch.llmBackend = "claude-cli";
|
|
682
681
|
await configStore.save(patch);
|
|
683
682
|
log("");
|
|
684
|
-
log('✓
|
|
683
|
+
log('✓ All set — using your Claude login. Try "what time zone is Tokyo in?", then');
|
|
685
684
|
log(' "send an email to my boss saying I quit" to see the approval gate.');
|
|
686
685
|
log("");
|
|
687
686
|
return { ...persisted, ...patch };
|
|
688
687
|
}
|
|
688
|
+
log("");
|
|
689
|
+
} else {
|
|
690
|
+
log("Tip: if you already use Claude Code, install it and sign in (run `claude` once), then");
|
|
691
|
+
log("start Aielia again — you can use it with no API key. Otherwise, pick a provider below.");
|
|
692
|
+
log("");
|
|
689
693
|
}
|
|
690
|
-
log("
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
+
log("Which AI provider do you have (or want to use)?");
|
|
695
|
+
log("");
|
|
696
|
+
PROVIDER_SETUP.forEach((p, i) => {
|
|
697
|
+
log(` ${i + 1}) ${p.name}`);
|
|
698
|
+
log(` ${p.blurb}`);
|
|
699
|
+
});
|
|
700
|
+
log("");
|
|
701
|
+
const choice = (await ask(`Type 1-${PROVIDER_SETUP.length} and press Enter (or just Enter to skip): `)).trim();
|
|
702
|
+
const picked = PROVIDER_SETUP[Number(choice) - 1];
|
|
694
703
|
if (!picked) {
|
|
695
704
|
log("");
|
|
696
|
-
log('Skipped.
|
|
697
|
-
log("on :8787).
|
|
698
|
-
log("/config set
|
|
705
|
+
log('Skipped. Until you set a provider, Aielia will try the "proxy" backend (needs');
|
|
706
|
+
log("@buildaharness/proxy running on :8787). To set one up later, run:");
|
|
707
|
+
log(" /config set llmBackend <anthropic|openai|openrouter>");
|
|
708
|
+
log(" /config set apiKey <your key>");
|
|
699
709
|
log("");
|
|
700
710
|
return persisted;
|
|
701
711
|
}
|
|
702
|
-
|
|
712
|
+
log("");
|
|
713
|
+
log(`To connect ${picked.name}, you need an “API key” — a password-like code that lets Aielia use your account.`);
|
|
714
|
+
log("Here’s how to get one:");
|
|
715
|
+
log("");
|
|
716
|
+
picked.steps.forEach((step, i) => log(` ${i + 1}. ${step}`));
|
|
717
|
+
if (picked.safety) {
|
|
718
|
+
log("");
|
|
719
|
+
log(` ${picked.safety.title}:`);
|
|
720
|
+
picked.safety.steps.forEach((step, i) => log(` ${i + 1}. ${step}`));
|
|
721
|
+
log(` (${picked.safety.urlLabel})`);
|
|
722
|
+
}
|
|
723
|
+
log("");
|
|
724
|
+
log(`The key starts with "${picked.keyPrefix}". Paste it below when you have it.`);
|
|
725
|
+
log("");
|
|
726
|
+
let key = "";
|
|
727
|
+
for (let attempt = 1; attempt <= MAX_KEY_ATTEMPTS; attempt++) {
|
|
728
|
+
const candidate = cleanApiKey(await ask("Paste your API key (or just Enter to skip): "));
|
|
729
|
+
if (candidate === "") break;
|
|
730
|
+
const formatProblem = checkApiKeyFormat(picked.backend, candidate);
|
|
731
|
+
if (formatProblem) {
|
|
732
|
+
log(` ✗ ${formatProblem}`);
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
if (testKey) {
|
|
736
|
+
log(" Checking your key…");
|
|
737
|
+
const result = await testKey(picked.backend, candidate);
|
|
738
|
+
if (result.status === "invalid") {
|
|
739
|
+
log(` ✗ ${result.message}`);
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (result.status === "unverified") log(` ! ${result.message}`);
|
|
743
|
+
}
|
|
744
|
+
key = candidate;
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
703
747
|
if (!key) {
|
|
704
748
|
log("");
|
|
705
|
-
log("No key entered
|
|
749
|
+
log("No working key entered, so nothing was saved. Run Aielia again to retry, or use");
|
|
750
|
+
log("/config set llmBackend " + picked.backend + " then /config set apiKey <key>");
|
|
706
751
|
log("");
|
|
707
752
|
return persisted;
|
|
708
753
|
}
|
|
@@ -710,8 +755,9 @@ async function maybeRunFirstRunSetup(deps) {
|
|
|
710
755
|
patch.apiKey = key;
|
|
711
756
|
await configStore.save(patch);
|
|
712
757
|
log("");
|
|
713
|
-
log(`✓
|
|
714
|
-
log("
|
|
758
|
+
log(`✓ You’re all set — using ${picked.name}. Say hello!`);
|
|
759
|
+
log(" Your key is saved on this computer in ~/.buildaharness/personal-assistant/config.json");
|
|
760
|
+
log(" (plain text, like a .env file) — don’t share that file.");
|
|
715
761
|
log("");
|
|
716
762
|
return { ...persisted, ...patch };
|
|
717
763
|
}
|
|
@@ -1088,6 +1134,10 @@ async function buildAssistant(config, { backend, dataDir, remindersFile }) {
|
|
|
1088
1134
|
// chat-ui, and the Tauri desktop build all decide this the same way. Undefined here means
|
|
1089
1135
|
// PersonalAssistant falls back to DEFAULT_ONE_LOOP_MODE.
|
|
1090
1136
|
oneLoopMode: config.oneLoopMode,
|
|
1137
|
+
// R7: turn-end next-step options and DONE-thread suggestions. Undefined config means the
|
|
1138
|
+
// package default (enabled); PersonalAssistant itself defaults to disabled, so this is where
|
|
1139
|
+
// the front end opts in.
|
|
1140
|
+
goalGraphSuggestMode: isGoalGraphSuggestEnabled(config.goalGraphSuggestMode) ? "enabled" : "disabled",
|
|
1091
1141
|
// P11 of the internal plan — resolved through the same shared config
|
|
1092
1142
|
// seam as oneLoopMode above (ASSISTANT_PLAN_MODE / `/config set planMode`). Undefined here
|
|
1093
1143
|
// means PersonalAssistant falls back to DEFAULT_PLAN_MODE ('legacy').
|
|
@@ -1124,7 +1174,8 @@ async function runCli(options = {}) {
|
|
|
1124
1174
|
ask: firstRunAsk,
|
|
1125
1175
|
detectClaudeCli: options.detectClaudeCli ?? (async () => (await checkClaudeCli(process.env.CLAUDE_PATH ?? "claude")).ok),
|
|
1126
1176
|
log: (line) => outputStream.write(`${line}
|
|
1127
|
-
`)
|
|
1177
|
+
`),
|
|
1178
|
+
testKey: options.firstRunTestKey ?? ((backend2, key) => testApiKey(backend2, key))
|
|
1128
1179
|
});
|
|
1129
1180
|
} finally {
|
|
1130
1181
|
setupRl == null ? void 0 : setupRl.close();
|
|
@@ -1496,6 +1547,12 @@ ${formatMemoryPendingOutcome("forgotten", outcome)}
|
|
|
1496
1547
|
const hits = await assistant.searchTranscript(query);
|
|
1497
1548
|
console.log(`
|
|
1498
1549
|
${formatSearchResults(hits, query)}
|
|
1550
|
+
`);
|
|
1551
|
+
}
|
|
1552
|
+
async function handleGoals() {
|
|
1553
|
+
const state = await assistant.getGoalGraphState("cli");
|
|
1554
|
+
console.log(`
|
|
1555
|
+
${formatGoalGraphState(state)}
|
|
1499
1556
|
`);
|
|
1500
1557
|
}
|
|
1501
1558
|
async function printCost() {
|
|
@@ -1734,7 +1791,15 @@ Unrecognized input "${token}".
|
|
|
1734
1791
|
if (!streamedAnyTokens) writeProgress(progress);
|
|
1735
1792
|
},
|
|
1736
1793
|
onToken: writeToken,
|
|
1737
|
-
onToolStep: writeToolStep
|
|
1794
|
+
onToolStep: writeToolStep,
|
|
1795
|
+
// Phase 4 — real intra-turn absorption via checkCallerUpdates, replacing Phase 3's
|
|
1796
|
+
// drain-as-follow-up-turns fallback in dispatchOne's `finally` below (kept there, but
|
|
1797
|
+
// only as a backstop: turn() re-enqueues anything it didn't get around to classifying
|
|
1798
|
+
// this turn — e.g. a trivial turn that never calls harnessBridge.run() at all — back onto
|
|
1799
|
+
// this same channel, see TurnOptions.steeringChannel's doc comment). Passed unconditionally:
|
|
1800
|
+
// with goalGraphMode off, routeMessage (below) never enqueues into steeringChannel, so it's
|
|
1801
|
+
// always empty and this resolves to exactly today's behavior (INV-43).
|
|
1802
|
+
steeringChannel
|
|
1738
1803
|
});
|
|
1739
1804
|
if (!streamedAnyTokens) clearProgress();
|
|
1740
1805
|
if (result.status === "needs_approval" && result.pendingActionId) {
|
|
@@ -1804,16 +1869,20 @@ Unrecognized input "${token}".
|
|
|
1804
1869
|
const contradictionNotice = result.contradictionNotice ? `
|
|
1805
1870
|
|
|
1806
1871
|
${result.contradictionNotice}` : "";
|
|
1872
|
+
lastNextSteps = result.nextSteps && result.nextSteps.length > 0 ? result.nextSteps : void 0;
|
|
1873
|
+
const nextStepsBlock = lastNextSteps ? `
|
|
1874
|
+
|
|
1875
|
+
${formatNextSteps(lastNextSteps)}` : "";
|
|
1807
1876
|
if (streamedAnyTokens) {
|
|
1808
1877
|
const pausedNoteText = result.pausedNote ? `
|
|
1809
1878
|
|
|
1810
1879
|
${result.pausedNote}` : "";
|
|
1811
|
-
process.stdout.write(`${pausedNoteText}${riskSuffix}${sourcesHint}${planHint}${contradictionNotice}
|
|
1880
|
+
process.stdout.write(`${pausedNoteText}${riskSuffix}${sourcesHint}${planHint}${contradictionNotice}${nextStepsBlock}
|
|
1812
1881
|
|
|
1813
1882
|
`);
|
|
1814
1883
|
} else {
|
|
1815
1884
|
console.log(`
|
|
1816
|
-
Aielia>${riskSuffix} ${result.reply}${sourcesHint}${planHint}${contradictionNotice}
|
|
1885
|
+
Aielia>${riskSuffix} ${result.reply}${sourcesHint}${planHint}${contradictionNotice}${nextStepsBlock}
|
|
1817
1886
|
`);
|
|
1818
1887
|
}
|
|
1819
1888
|
} catch (err) {
|
|
@@ -1987,6 +2056,9 @@ ${assistant.getActiveProject() || "(none)"}
|
|
|
1987
2056
|
}
|
|
1988
2057
|
await handleConfigCommand(["set", "activeProject", args.join(" ")]);
|
|
1989
2058
|
}
|
|
2059
|
+
let turnInProgress = false;
|
|
2060
|
+
let lastNextSteps;
|
|
2061
|
+
const steeringChannel = new LiveSteeringChannel();
|
|
1990
2062
|
const commands = {
|
|
1991
2063
|
"/why": () => printWhy(),
|
|
1992
2064
|
"/layers": () => printLayers(),
|
|
@@ -2001,6 +2073,7 @@ ${assistant.getActiveProject() || "(none)"}
|
|
|
2001
2073
|
"/undo-action": (args) => handleUndoAction(args),
|
|
2002
2074
|
"/memory": (args) => handleMemory(args),
|
|
2003
2075
|
"/search": (args) => handleSearch(args),
|
|
2076
|
+
"/goals": () => handleGoals(),
|
|
2004
2077
|
"/model": (args) => handleModel(args),
|
|
2005
2078
|
"/project": (args) => handleProject(args),
|
|
2006
2079
|
"/cost": () => printCost(),
|
|
@@ -2009,13 +2082,29 @@ ${assistant.getActiveProject() || "(none)"}
|
|
|
2009
2082
|
"/checkpoint": (args) => handleCheckpoint(args)
|
|
2010
2083
|
};
|
|
2011
2084
|
async function dispatchOne(message) {
|
|
2085
|
+
const pick = /^[1-3]$/.test(message) && lastNextSteps ? lastNextSteps[Number(message) - 1] : void 0;
|
|
2086
|
+
lastNextSteps = void 0;
|
|
2087
|
+
if (pick) {
|
|
2088
|
+
console.log(` → ${pick.description}`);
|
|
2089
|
+
message = pick.description;
|
|
2090
|
+
}
|
|
2012
2091
|
const [token, ...args] = message.split(/\s+/);
|
|
2013
2092
|
const handler = commands[token];
|
|
2014
2093
|
if (handler) {
|
|
2015
2094
|
await handler(args);
|
|
2016
2095
|
return;
|
|
2017
2096
|
}
|
|
2018
|
-
|
|
2097
|
+
turnInProgress = true;
|
|
2098
|
+
try {
|
|
2099
|
+
await handleTurn(message);
|
|
2100
|
+
} finally {
|
|
2101
|
+
turnInProgress = false;
|
|
2102
|
+
if (isGoalGraphEnabled(config.goalGraphMode)) {
|
|
2103
|
+
for (const event of steeringChannel.poll()) {
|
|
2104
|
+
void enqueue(event.message);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2019
2108
|
}
|
|
2020
2109
|
let dispatchQueue = Promise.resolve();
|
|
2021
2110
|
function enqueue(message) {
|
|
@@ -2025,20 +2114,32 @@ ${assistant.getActiveProject() || "(none)"}
|
|
|
2025
2114
|
dispatchQueue = result;
|
|
2026
2115
|
return result;
|
|
2027
2116
|
}
|
|
2117
|
+
function isKnownCommand(message) {
|
|
2118
|
+
const [token] = message.split(/\s+/);
|
|
2119
|
+
return token in commands;
|
|
2120
|
+
}
|
|
2121
|
+
function routeMessage(message) {
|
|
2122
|
+
if (isGoalGraphEnabled(config.goalGraphMode) && turnInProgress && !isKnownCommand(message)) {
|
|
2123
|
+
steeringChannel.enqueue(message);
|
|
2124
|
+
console.log("\n[queued — the current turn is still running; this will be taken into account once it finishes]\n");
|
|
2125
|
+
return Promise.resolve();
|
|
2126
|
+
}
|
|
2127
|
+
return enqueue(message);
|
|
2128
|
+
}
|
|
2028
2129
|
rl.on("line", (line) => {
|
|
2029
2130
|
const message = line.trim();
|
|
2030
2131
|
if (!message) {
|
|
2031
2132
|
rl.prompt();
|
|
2032
2133
|
return;
|
|
2033
2134
|
}
|
|
2034
|
-
void
|
|
2135
|
+
void routeMessage(message).finally(() => rl.prompt());
|
|
2035
2136
|
});
|
|
2036
2137
|
rl.resume();
|
|
2037
2138
|
return {
|
|
2038
2139
|
dispatchLine: async (line) => {
|
|
2039
2140
|
const message = line.trim();
|
|
2040
2141
|
if (!message) return;
|
|
2041
|
-
await
|
|
2142
|
+
await routeMessage(message);
|
|
2042
2143
|
},
|
|
2043
2144
|
close: () => rl.close(),
|
|
2044
2145
|
getStatusIndicators: async () => {
|
|
@@ -2081,7 +2182,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
2081
2182
|
});
|
|
2082
2183
|
}
|
|
2083
2184
|
if (shouldLaunchTuiApp(config.tuiMode ?? "disabled", Boolean(process.stdout.isTTY), Boolean(process.stdin.isTTY))) {
|
|
2084
|
-
const { runTuiApp } = await import("./tui-app-
|
|
2185
|
+
const { runTuiApp } = await import("./tui-app-DeyZ4Pr9.js");
|
|
2085
2186
|
await runTuiApp();
|
|
2086
2187
|
return;
|
|
2087
2188
|
}
|
|
@@ -2105,4 +2206,4 @@ export {
|
|
|
2105
2206
|
PLAN_LINE_PREFIX as P,
|
|
2106
2207
|
runCli as r
|
|
2107
2208
|
};
|
|
2108
|
-
//# sourceMappingURL=bin-
|
|
2209
|
+
//# sourceMappingURL=bin-CdVTAe7j.js.map
|