@hoilab/ada-cli 0.84.17 → 0.84.19

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.
@@ -39,6 +39,7 @@ import { createLocalBashOperations } from "./tools/bash.js";
39
39
  import { createAllToolDefinitions } from "./tools/index.js";
40
40
  import { createMemoryEngineTools } from "./memory-engine/tools.js";
41
41
  import { redactSensitive } from "./memory-engine/security.js";
42
+ import { expandVaultRefs } from "./memory-engine/vault.js";
42
43
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
43
44
  import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
44
45
  /**
@@ -379,6 +380,25 @@ export class AgentSession {
379
380
  // =========================================================================
380
381
  // Automatic project memory (claude-mem style)
381
382
  // =========================================================================
383
+ /**
384
+ * Replace vault-expanded secrets with the redacted form for UI events and
385
+ * persistence (the LLM still receives the expanded values via agent state).
386
+ */
387
+ _applyVaultRedaction(event) {
388
+ if (event.message.role !== "user")
389
+ return;
390
+ const id = event.message.id;
391
+ if (!id)
392
+ return;
393
+ const redactedText = this._vaultRedactions.get(id);
394
+ if (redactedText === undefined)
395
+ return;
396
+ this._vaultRedactions.delete(id);
397
+ event.message = {
398
+ ...event.message,
399
+ content: [{ type: "text", text: redactedText }],
400
+ };
401
+ }
382
402
  /** Live-index a message for the Memory Engine session search (layer 3c). */
383
403
  _indexMessageForMemory(message) {
384
404
  const memoryEngine = this._resourceLoader.getMemoryEngine();
@@ -468,6 +488,11 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
468
488
  // Track last assistant message for auto-compaction check
469
489
  _lastAssistantMessage = undefined;
470
490
  _lastAutoMemorizeAt = 0;
491
+ /** Vault expansion redactions by user-message id (frontend + persistence). */
492
+ _vaultRedactions = new Map();
493
+ _userMessageSeq = 0;
494
+ /** Pending expansion for the user message built right after (prompt flow). */
495
+ _pendingVaultExpansion = null;
471
496
  /** Internal handler for agent events - shared by subscribe and reconnect */
472
497
  _handleAgentEvent = async (event) => {
473
498
  // When a user message starts, check if it's from either queue and remove it BEFORE emitting
@@ -527,7 +552,8 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
527
552
  else if (event.message.role === "user" ||
528
553
  event.message.role === "assistant" ||
529
554
  event.message.role === "toolResult") {
530
- // Regular LLM message - persist as SessionMessageEntry
555
+ // Regular LLM message - persist as SessionMessageEntry. Vault
556
+ // expansions are already redacted by _applyVaultRedaction above.
531
557
  this.sessionManager.appendMessage(event.message);
532
558
  }
533
559
  // Other message types (bashExecution, compactionSummary, branchSummary) are persisted elsewhere
@@ -617,6 +643,7 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
617
643
  this._turnIndex++;
618
644
  }
619
645
  else if (event.type === "message_start") {
646
+ this._applyVaultRedaction(event);
620
647
  const extensionEvent = {
621
648
  type: "message_start",
622
649
  message: event.message,
@@ -632,6 +659,7 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
632
659
  await this._extensionRunner.emit(extensionEvent);
633
660
  }
634
661
  else if (event.type === "message_end") {
662
+ this._applyVaultRedaction(event);
635
663
  const extensionEvent = {
636
664
  type: "message_end",
637
665
  message: event.message,
@@ -1010,6 +1038,20 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1010
1038
  memoryEngine.checkUserTextForSensitive(expandedText);
1011
1039
  expandedText = redactSensitive(expandedText, memoryEngine.privacyLevel);
1012
1040
  }
1041
+ // Secure Vault: expand @vault:<name> references so the LLM can use
1042
+ // secrets without them ever being persisted. The redacted text is
1043
+ // kept for session persistence (applied in message_end).
1044
+ const vaultManager = this._resourceLoader.getVaultManager();
1045
+ if (vaultManager) {
1046
+ const expansion = expandVaultRefs(expandedText, vaultManager);
1047
+ if (expansion.expanded.length > 0) {
1048
+ this._pendingVaultExpansion = {
1049
+ id: `usr-vault-${++this._userMessageSeq}`,
1050
+ redactedText: expandedText,
1051
+ };
1052
+ expandedText = expansion.text;
1053
+ }
1054
+ }
1013
1055
  // If streaming, queue via steer() or followUp() based on option
1014
1056
  if (this.isStreaming) {
1015
1057
  if (!options?.streamingBehavior) {
@@ -1054,11 +1096,18 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1054
1096
  if (currentImages) {
1055
1097
  userContent.push(...currentImages);
1056
1098
  }
1057
- messages.push({
1099
+ const vaultExpansion = this._pendingVaultExpansion;
1100
+ this._pendingVaultExpansion = null;
1101
+ const userMessage = {
1102
+ ...(vaultExpansion ? { id: vaultExpansion.id } : {}),
1058
1103
  role: "user",
1059
1104
  content: userContent,
1060
1105
  timestamp: Date.now(),
1061
- });
1106
+ };
1107
+ if (vaultExpansion) {
1108
+ this._vaultRedactions.set(vaultExpansion.id, vaultExpansion.redactedText);
1109
+ }
1110
+ messages.push(userMessage);
1062
1111
  // Inject any pending "nextTurn" messages as context alongside the user message
1063
1112
  for (const msg of this._pendingNextTurnMessages) {
1064
1113
  messages.push(msg);
@@ -1203,11 +1252,18 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1203
1252
  if (images) {
1204
1253
  content.push(...images);
1205
1254
  }
1206
- this.agent.steer({
1255
+ const vaultExpansion = this._pendingVaultExpansion;
1256
+ this._pendingVaultExpansion = null;
1257
+ const message = {
1258
+ ...(vaultExpansion ? { id: vaultExpansion.id } : {}),
1207
1259
  role: "user",
1208
1260
  content,
1209
1261
  timestamp: Date.now(),
1210
- });
1262
+ };
1263
+ if (vaultExpansion) {
1264
+ this._vaultRedactions.set(vaultExpansion.id, vaultExpansion.redactedText);
1265
+ }
1266
+ this.agent.steer(message);
1211
1267
  }
1212
1268
  /**
1213
1269
  * Internal: Queue a follow-up message (already expanded, no extension command check).
@@ -1219,11 +1275,19 @@ Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COM
1219
1275
  if (images) {
1220
1276
  content.push(...images);
1221
1277
  }
1222
- this.agent.followUp({
1278
+ const vaultExpansion = this._pendingVaultExpansion;
1279
+ this._pendingVaultExpansion = null;
1280
+ const message = {
1281
+ ...(vaultExpansion ? { id: vaultExpansion.id } : {}),
1223
1282
  role: "user",
1224
1283
  content,
1225
1284
  timestamp: Date.now(),
1226
- });
1285
+ };
1286
+ if (vaultExpansion) {
1287
+ this._vaultRedactions.set(vaultExpansion.id, vaultExpansion.redactedText);
1288
+ }
1289
+ this.agent.followUp(message);
1290
+ return;
1227
1291
  }
1228
1292
  /**
1229
1293
  * Throw an error if the text is an extension command.