@staff0rd/assist 0.496.0 → 0.496.2

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/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.496.0",
9
+ version: "0.496.2",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -10848,6 +10848,46 @@ function toSegment(part) {
10848
10848
  return typeof part === "number" ? { kind: "index", index: part } : { kind: "key", name: String(part) };
10849
10849
  }
10850
10850
 
10851
+ // src/shared/maskConfigSecrets.ts
10852
+ var SECRET_MASK = "<hidden>";
10853
+ function maskConfigSecrets(value, node) {
10854
+ return mapConfigSecrets(value, node, () => SECRET_MASK);
10855
+ }
10856
+
10857
+ // src/shared/scrubConfigSecrets.ts
10858
+ function secretStrings(value, node) {
10859
+ const secrets = [];
10860
+ mapConfigSecrets(value, node, (secret) => {
10861
+ if (typeof secret === "string" && secret !== "") secrets.push(secret);
10862
+ return secret;
10863
+ });
10864
+ return secrets;
10865
+ }
10866
+ function literalAndJsonEncoded(secret) {
10867
+ const encoded = JSON.stringify(secret).slice(1, -1);
10868
+ return encoded === secret ? [secret] : [secret, encoded];
10869
+ }
10870
+ function scrubConfigSecrets(messages, value, node) {
10871
+ const needles = secretStrings(value, node).flatMap(literalAndJsonEncoded).sort((a, b) => b.length - a.length);
10872
+ if (needles.length === 0) return messages;
10873
+ return messages.map(
10874
+ (message3) => needles.reduce(
10875
+ (text17, needle) => text17.split(needle).join(SECRET_MASK),
10876
+ message3
10877
+ )
10878
+ );
10879
+ }
10880
+
10881
+ // src/commands/config/configKeyNode.ts
10882
+ function configKeyNode(key) {
10883
+ return configEntryNode(describeConfigNode(assistConfigSchema), key);
10884
+ }
10885
+
10886
+ // src/commands/config/scrubConfigKeyError.ts
10887
+ function scrubConfigKeyError(error, key, value) {
10888
+ return scrubConfigSecrets([error], value, configKeyNode(key))[0];
10889
+ }
10890
+
10851
10891
  // src/commands/config/coerceConfigValue.ts
10852
10892
  var EDITABLE_TYPES = /* @__PURE__ */ new Set([
10853
10893
  "string",
@@ -10857,6 +10897,11 @@ var EDITABLE_TYPES = /* @__PURE__ */ new Set([
10857
10897
  "union"
10858
10898
  ]);
10859
10899
  function coerceConfigValue(key, raw) {
10900
+ const result = coerceValue(key, raw);
10901
+ if (result.ok) return result;
10902
+ return { ok: false, error: scrubConfigKeyError(result.error, key, raw) };
10903
+ }
10904
+ function coerceValue(key, raw) {
10860
10905
  const leaf = describeConfigLeaves(assistConfigSchema).find(
10861
10906
  (candidate) => candidate.key === key
10862
10907
  );
@@ -10917,7 +10962,7 @@ function restoreConfigSecrets(value, stored, node) {
10917
10962
 
10918
10963
  // src/commands/config/restoreConfigWriteSecrets.ts
10919
10964
  function restoreConfigWriteSecrets(key, value, cwd) {
10920
- const node = configEntryNode(describeConfigNode(assistConfigSchema), key);
10965
+ const node = configKeyNode(key);
10921
10966
  if (!node) return value;
10922
10967
  const stored = loadConfigFrom(cwd);
10923
10968
  return restoreConfigSecrets(value, getNestedValue(stored, key), node);
@@ -10975,9 +11020,10 @@ function setNestedValue(obj, path71, value) {
10975
11020
  function validateConfig(updated, key, schema2 = assistConfigSchema) {
10976
11021
  const result = schema2.safeParse(updated);
10977
11022
  if (result.success) return { ok: true };
11023
+ const errors = result.error.issues.map((issue) => formatIssue2(issue, key));
10978
11024
  return {
10979
11025
  ok: false,
10980
- errors: result.error.issues.map((issue) => formatIssue2(issue, key))
11026
+ errors: scrubConfigSecrets(errors, updated, describeConfigNode(schema2))
10981
11027
  };
10982
11028
  }
10983
11029
  function formatIssue2(issue, key) {
@@ -15121,10 +15167,17 @@ function parsePerms(entries) {
15121
15167
  return map;
15122
15168
  }
15123
15169
 
15170
+ // src/shared/revealsConfigSecret.ts
15171
+ var REVEAL_RE = /^assist\s+config\s+get\b.*\s--reveal\b/;
15172
+ function revealsConfigSecret(command) {
15173
+ return REVEAL_RE.test(command);
15174
+ }
15175
+
15124
15176
  // src/shared/isApprovedRead.ts
15125
15177
  var READ_RE = /^Read\((.+)\)$/;
15126
15178
  var SAFE_BUILTINS = /* @__PURE__ */ new Set(["true", "false"]);
15127
15179
  function isApprovedRead(command, toolName = "Bash") {
15180
+ if (revealsConfigSecret(command)) return void 0;
15128
15181
  if (SAFE_BUILTINS.has(command)) return `safe shell builtin: ${command}`;
15129
15182
  if (isCdToCwd(command)) return "cd to current directory";
15130
15183
  const cdRead = isCdToReadAllowedDir(command);
@@ -16671,14 +16724,6 @@ function registerComplexity(program2) {
16671
16724
 
16672
16725
  // src/commands/config/index.ts
16673
16726
  import { stringify as stringifyYaml2 } from "yaml";
16674
-
16675
- // src/shared/maskConfigSecrets.ts
16676
- var SECRET_MASK = "<hidden>";
16677
- function maskConfigSecrets(value, node) {
16678
- return mapConfigSecrets(value, node, () => SECRET_MASK);
16679
- }
16680
-
16681
- // src/commands/config/index.ts
16682
16727
  function configList() {
16683
16728
  const config = maskConfigSecrets(
16684
16729
  loadConfig(),
@@ -16689,16 +16734,21 @@ function configList() {
16689
16734
 
16690
16735
  // src/commands/config/configGet.ts
16691
16736
  import chalk131 from "chalk";
16737
+
16738
+ // src/commands/config/maskConfigKeySecrets.ts
16739
+ function maskConfigKeySecrets(key, value) {
16740
+ return maskConfigSecrets(value, configKeyNode(key));
16741
+ }
16742
+
16743
+ // src/commands/config/configGet.ts
16692
16744
  function configGet(key, options2 = {}) {
16693
16745
  const value = requireNestedValue(
16694
16746
  loadConfig(),
16695
16747
  key
16696
16748
  );
16697
- console.log(formatOutput(options2.reveal ? value : maskSecretsAt(value, key)));
16698
- }
16699
- function maskSecretsAt(value, key) {
16700
- const node = configEntryNode(describeConfigNode(assistConfigSchema), key);
16701
- return maskConfigSecrets(value, node);
16749
+ console.log(
16750
+ formatOutput(options2.reveal ? value : maskConfigKeySecrets(key, value))
16751
+ );
16702
16752
  }
16703
16753
  function formatOutput(value) {
16704
16754
  return typeof value === "object" && value !== null ? JSON.stringify(value, null, 2) : String(value);
@@ -16769,9 +16819,8 @@ function configSet(key, value, options2 = {}) {
16769
16819
  if (!coercion.ok) exitWithConfigErrors([coercion.error]);
16770
16820
  const coerced = coercion.value;
16771
16821
  const target = resolved.useRepo ? `repo: ${applyRepoOrExit(resolved.key, coerced, resolved.repoName)}` : applyOrExit(resolved.key, coerced, options2.global ?? false);
16772
- console.log(
16773
- chalk133.green(`Set ${resolved.key} = ${JSON.stringify(coerced)} (${target})`)
16774
- );
16822
+ const shown = JSON.stringify(maskConfigKeySecrets(resolved.key, coerced));
16823
+ console.log(chalk133.green(`Set ${resolved.key} = ${shown} (${target})`));
16775
16824
  }
16776
16825
  function applyOrExit(key, coerced, global) {
16777
16826
  const result = applyConfigSet(key, coerced, global);
@@ -30192,12 +30241,11 @@ function deriveTranscriptStatus(entries, opts = {}) {
30192
30241
  if (ev.kind === "toolResult") return null;
30193
30242
  if (ev.stopReason != null && TURN_END_STOP_REASONS.has(ev.stopReason))
30194
30243
  return "waiting";
30244
+ if (opts.permissionActive) return "waiting";
30195
30245
  const resolved = resolvedToolUseIds(events, lastIdx);
30196
30246
  const pending = ev.toolUses.filter((tool) => !resolved.has(tool.id));
30197
- if (pending.length === 0) return "running";
30198
30247
  if (pending.some((tool) => tool.name === ASK_USER_QUESTION2))
30199
30248
  return "waiting";
30200
- if (opts.permissionActive) return "waiting";
30201
30249
  return "running";
30202
30250
  }
30203
30251
  function lastDecisiveIndex(events) {
@@ -30275,6 +30323,19 @@ function readTranscriptTailSync(filePath, maxBytes = DEFAULT_MAX_BYTES) {
30275
30323
  }
30276
30324
  }
30277
30325
 
30326
+ // src/commands/sessions/shared/transcriptTailFingerprint.ts
30327
+ var CONVERSATION_TYPES = /* @__PURE__ */ new Set(["user", "assistant"]);
30328
+ function transcriptTailFingerprint(entries) {
30329
+ for (let i = entries.length - 1; i >= 0; i--) {
30330
+ const entry = entries[i];
30331
+ if (entry.isSidechain || entry.isMeta) continue;
30332
+ if (typeof entry.type !== "string" || !CONVERSATION_TYPES.has(entry.type))
30333
+ continue;
30334
+ return typeof entry.uuid === "string" && entry.uuid ? entry.uuid : null;
30335
+ }
30336
+ return null;
30337
+ }
30338
+
30278
30339
  // src/commands/sessions/daemon/reconcileTranscriptStatus.ts
30279
30340
  async function resolveTranscriptPath(session) {
30280
30341
  if (session.transcriptPath) return session.transcriptPath;
@@ -30290,11 +30351,14 @@ async function reconcileTranscriptStatus(session, onStatusChange) {
30290
30351
  const filePath = await resolveTranscriptPath(session);
30291
30352
  if (!filePath) return;
30292
30353
  const entries = await readTranscriptTail(filePath);
30354
+ const fingerprint = transcriptTailFingerprint(entries);
30355
+ if (fingerprint !== null && fingerprint === session.transcriptFingerprint)
30356
+ return;
30357
+ session.transcriptFingerprint = fingerprint ?? void 0;
30293
30358
  const derived = deriveTranscriptStatus(entries, {
30294
30359
  permissionActive: session.permissionActive
30295
30360
  });
30296
30361
  if (!derived) return;
30297
- if (derived === "running") session.permissionActive = false;
30298
30362
  if (derived === session.status) return;
30299
30363
  daemonLog(
30300
30364
  `session ${session.id} transcript reconcile: ${session.status} -> ${derived}`
@@ -30347,6 +30411,7 @@ function watchTranscript(session, notify2, onStatusChange) {
30347
30411
  session.transcriptWatcher = void 0;
30348
30412
  }
30349
30413
  session.transcriptPath = void 0;
30414
+ session.transcriptFingerprint = void 0;
30350
30415
  const fileName = `${session.claudeSessionId}.jsonl`;
30351
30416
  let timer = null;
30352
30417
  const run4 = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.496.0",
3
+ "version": "0.496.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {