@wirecat/cli-core 0.465.0 → 0.467.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +4 -2
  3. package/dist/cli/command-runs.d.ts +38 -10
  4. package/dist/cli/command-runs.js +51 -17
  5. package/dist/cli/context.d.ts +3 -0
  6. package/dist/cli/context.js +15 -3
  7. package/dist/cli/index.d.ts +3 -3
  8. package/dist/cli/index.js +1 -1
  9. package/dist/cli/messenger/account-command.js +3 -0
  10. package/dist/cli/messenger/context.d.ts +9 -0
  11. package/dist/cli/messenger/context.js +6 -4
  12. package/dist/cli/messenger/mcp-command.js +8 -1
  13. package/dist/cli/messenger/observed.js +10 -2
  14. package/dist/cli/messenger/report.js +48 -3
  15. package/dist/cli/messenger/serve-relay.d.ts +1 -0
  16. package/dist/cli/program.d.ts +1 -1
  17. package/dist/cli/program.js +7 -3
  18. package/dist/cli/settings.d.ts +19 -6
  19. package/dist/cli/settings.js +14 -3
  20. package/dist/cli/shared-command-exports.d.ts +12 -4
  21. package/dist/entities/account-profile.d.ts +2 -0
  22. package/dist/entities/account-profile.js +2 -0
  23. package/dist/entities/index.d.ts +40 -0
  24. package/dist/entities/recorded-run.d.ts +39 -0
  25. package/dist/entities/recorded-run.js +14 -1
  26. package/dist/mcp/agent.d.ts +3 -0
  27. package/dist/mcp/agent.js +2 -0
  28. package/dist/mcp/all-profiles.d.ts +12 -2
  29. package/dist/mcp/all-profiles.js +38 -8
  30. package/dist/mcp/bot/server.js +8 -3
  31. package/dist/mcp/caller.d.ts +38 -0
  32. package/dist/mcp/caller.js +95 -0
  33. package/dist/mcp/http/oauth.js +6 -2
  34. package/dist/mcp/server.js +19 -7
  35. package/dist/mcp/surface.js +11 -2
  36. package/dist/mcp/tool.d.ts +4 -1
  37. package/dist/mcp/tool.js +21 -11
  38. package/dist/runs/events.d.ts +16 -4
  39. package/dist/runs/events.js +4 -0
  40. package/dist/runs/log-attributes.js +7 -1
  41. package/dist/runs/log-events.js +5 -0
  42. package/dist/runs/recording.d.ts +10 -3
  43. package/dist/runs/recording.js +59 -15
  44. package/dist/runs/run-context.d.ts +55 -4
  45. package/dist/runs/run-context.js +34 -3
  46. package/dist/runs/run.d.ts +37 -11
  47. package/dist/runs/run.js +201 -52
  48. package/dist/runs/scope.d.ts +12 -0
  49. package/dist/runs/scope.js +6 -0
  50. package/dist/services/patience.js +5 -1
  51. package/package.json +1 -1
@@ -69,8 +69,8 @@ export const createProgram = ({ app, commands, configure, firstScreen }, { out,
69
69
  .option("--full", `${found} with their whole text and every provider field`)
70
70
  .option("--dry-run", "preview parsed arguments and permissions before running the action")
71
71
  .option("--yes", "go ahead without the question an ask level puts before a write")
72
- .option("--record", "keep this run, with the command as typed and secrets hidden; record_input false leaves the typed words out")
73
- .option("--no-record", "do not keep it, whatever the configuration says")
72
+ .option("--record", "keep this run even where the config says record false; record_input false leaves the typed words out")
73
+ .option("--no-record", "do not keep this run, even if it fails")
74
74
  .showHelpAfterError();
75
75
  for (const command of mounted)
76
76
  program.addCommand(command);
@@ -617,7 +617,8 @@ const keepFailure = async (failure, { definition, program, rest, argv, profile,
617
617
  program,
618
618
  argv,
619
619
  env,
620
- recordInput: settings ? settings.recordInput !== false : false,
620
+ recordInput: settings ? (settings.recordInput ?? true) : false,
621
+ caller: options.agent?.caller,
621
622
  }),
622
623
  // Settings that did not load never accepted the word, which may be a refused name or somebody's words.
623
624
  profile: settings ? namedProfile(settings) : undefined,
@@ -628,6 +629,9 @@ const keepFailure = async (failure, { definition, program, rest, argv, profile,
628
629
  streams: options.streams ?? processStreams,
629
630
  env,
630
631
  ...(settings ? { keepDays: settings.keepRunsForDays } : {}),
632
+ ...(settings?.keepSuccessfulRunsForDays === undefined
633
+ ? {}
634
+ : { keepSuccessDays: settings.keepSuccessfulRunsForDays }),
631
635
  }, async () => {
632
636
  throw failure;
633
637
  }).catch(() => { });
@@ -2,12 +2,15 @@ import { CliError } from "@wirecat/cli-base";
2
2
  import * as v from "valibot";
3
3
  import { type AISettings } from "../analysis/settings.js";
4
4
  import { type AppIdentity } from "../ports/app.js";
5
+ import type { RecordInput } from "../runs/run-context.js";
5
6
  import { type Level, type PermissionKey } from "../sends/permissions.js";
6
7
  import { type ConfigIssue } from "./config-issues.js";
7
8
  import type { CommandPermissions } from "./permission-keys.js";
8
9
  export declare const plain: (rule: string) => (issue: v.BaseIssue<unknown>) => string;
9
10
  export declare const count: v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>;
10
11
  export declare const flag: v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>;
12
+ /** `record_input`: true, false, or "failed" — the words typed kept only in a run that failed. */
13
+ export declare const recordInputSetting: v.UnionSchema<[v.BooleanSchema<undefined>, v.LiteralSchema<"failed", undefined>], (issue: v.BaseIssue<unknown>) => string>;
11
14
  /**
12
15
  * **What one messenger adds to the file**, merged into the one strict schema — a field an adapter
13
16
  * reads but the schema did not know would be refused as unknown. Each entry must be optional.
@@ -102,15 +105,21 @@ export interface Settings extends AISettings {
102
105
  * differently on purpose — milliseconds in the file, a duration with a unit on the command line.
103
106
  */
104
107
  commandTimeoutMs: number | undefined;
108
+ /** Every run is kept unless the config says `record: false` or the command says `--no-record`. */
105
109
  record: boolean;
106
110
  /**
107
- * A run record keeps the words typed, secrets hidden; off masks every value that may be content. Optional so a CLI
108
- * with settings of its own need not return it: left out, the words are kept.
111
+ * A run record keeps the words typed, secrets hidden; off masks every value that may be content; `"failed"` keeps
112
+ * them only in a run that failed. Optional so a CLI with settings of its own need not return it: left out, the words
113
+ * are kept.
109
114
  */
110
- recordInput?: boolean;
111
- /** A failed run is kept even unrecorded, unless recording was turned off by name. */
115
+ recordInput?: RecordInput;
116
+ /** A failed run is kept even unrecorded, unless `--no-record` turned recording off for this command. */
112
117
  keepFailedRuns: boolean;
113
118
  keepRunsForDays: number;
119
+ /** Successful runs go sooner than failed ones. Optional so a CLI with settings of its own need not return it. */
120
+ keepSuccessfulRunsForDays?: number;
121
+ /** The runs folder's size limit, in megabytes. */
122
+ runsSizeLimitMb?: number;
114
123
  /** The variable that made this process read-only, above every level and flag (`<PREFIX>_READ_ONLY`). */
115
124
  readOnlyLock?: string;
116
125
  /**
@@ -276,8 +285,10 @@ export declare const settingsFor: <T extends object = Record<never, never>>(app:
276
285
  color: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
277
286
  sender_colors: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
278
287
  record: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
279
- record_input: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
288
+ record_input: v.OptionalSchema<v.UnionSchema<[v.BooleanSchema<undefined>, v.LiteralSchema<"failed", undefined>], (issue: v.BaseIssue<unknown>) => string>, undefined>;
280
289
  keep_runs_for_days: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
290
+ keep_successful_runs_for_days: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
291
+ runs_size_limit_mb: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
281
292
  permissions: v.OptionalSchema<v.RecordSchema<v.GenericSchema<string>, v.PicklistSchema<readonly ["deny", "readonly", "ask", "allow"], (issue: v.PicklistIssue) => string>, (issue: v.BaseIssue<unknown>) => string>, undefined>;
282
293
  sends_per_hour: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
283
294
  requests_per_minute: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 0, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
@@ -350,8 +361,10 @@ export declare const settingsFor: <T extends object = Record<never, never>>(app:
350
361
  color: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
351
362
  sender_colors: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
352
363
  record: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
353
- record_input: v.OptionalSchema<v.BooleanSchema<(issue: v.BaseIssue<unknown>) => string>, undefined>;
364
+ record_input: v.OptionalSchema<v.UnionSchema<[v.BooleanSchema<undefined>, v.LiteralSchema<"failed", undefined>], (issue: v.BaseIssue<unknown>) => string>, undefined>;
354
365
  keep_runs_for_days: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
366
+ keep_successful_runs_for_days: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
367
+ runs_size_limit_mb: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
355
368
  permissions: v.OptionalSchema<v.RecordSchema<v.GenericSchema<string>, v.PicklistSchema<readonly ["deny", "readonly", "ask", "allow"], (issue: v.PicklistIssue) => string>, (issue: v.BaseIssue<unknown>) => string>, undefined>;
356
369
  sends_per_hour: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 1, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
357
370
  requests_per_minute: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<(issue: v.BaseIssue<unknown>) => string>, v.IntegerAction<number, (issue: v.BaseIssue<unknown>) => string>, v.MinValueAction<number, 0, (issue: v.BaseIssue<unknown>) => string>]>, undefined>;
@@ -5,6 +5,7 @@ import { AI_ENTRIES, resolveAISettings } from "../analysis/settings.js";
5
5
  import { parseDuration } from "../domain/duration.js";
6
6
  import { DEFAULT_RERANK_MODEL, RERANK_MODELS } from "../embeddings/models.js";
7
7
  import { envName, readOnlyLockOf } from "../ports/app.js";
8
+ import { KEEP_SUCCESSFUL_RUNS_FOR_DAYS, RUNS_SIZE_LIMIT_MB } from "../runs/run.js";
8
9
  import { assertOverridesTighten, CEILING_KEY, COMMAND_PATH_KEY, LEVELS, layerPermissions, permissionOverrides, RESOURCE_KEY, RESOURCES, } from "../sends/permissions.js";
9
10
  import { storePath } from "../store/path.js";
10
11
  import { sharedConfigPath } from "../store/shared-config.js";
@@ -33,6 +34,8 @@ export const count = v.pipe(v.number(wholeNumber), v.integer(wholeNumber), v.min
33
34
  const zeroOrMore = plain("has to be a whole number, 0 or more");
34
35
  const countOrZero = v.pipe(v.number(zeroOrMore), v.integer(zeroOrMore), v.minValue(0, zeroOrMore));
35
36
  export const flag = v.boolean(plain("has to be true or false"));
37
+ /** `record_input`: true, false, or "failed" — the words typed kept only in a run that failed. */
38
+ export const recordInputSetting = v.union([v.boolean(), v.literal("failed")], plain('has to be true, false or "failed"'));
36
39
  const wholeOrZero = (text, name) => {
37
40
  const value = Number(text);
38
41
  if (!Number.isInteger(value) || value < 0)
@@ -58,8 +61,10 @@ const SHARED_PROFILE_ENTRIES = {
58
61
  color: v.optional(flag),
59
62
  sender_colors: v.optional(flag),
60
63
  record: v.optional(flag),
61
- record_input: v.optional(flag),
64
+ record_input: v.optional(recordInputSetting),
62
65
  keep_runs_for_days: v.optional(count),
66
+ keep_successful_runs_for_days: v.optional(count),
67
+ runs_size_limit_mb: v.optional(count),
63
68
  permissions: v.optional(permissionLevels),
64
69
  sends_per_hour: v.optional(count),
65
70
  requests_per_minute: v.optional(countOrZero),
@@ -232,7 +237,7 @@ export const settingsFor = (app, extension = {}) => {
232
237
  const fromLayers = (key, fallback, only = layers) => first(only.map(([from, scope]) => [from, scope?.[key]]), fallback);
233
238
  const kindLayers = layers.filter(([from]) => from.startsWith(`config file: ${kind}.`));
234
239
  const limit = flags.limit === undefined ? fromLayers("limit", DEFAULT_LIMIT) : { value: flags.limit, from: "flag" };
235
- const record = flags.record === undefined ? fromLayers("record", false) : { value: flags.record, from: "flag" };
240
+ const record = flags.record === undefined ? fromLayers("record", true) : { value: flags.record, from: "flag" };
236
241
  const timeoutMs = fromLayers("timeout_ms", undefined);
237
242
  const color = fromLayers("color", undefined);
238
243
  const senderColors = fromLayers("sender_colors", false);
@@ -241,6 +246,8 @@ export const settingsFor = (app, extension = {}) => {
241
246
  const searchReranker = fromLayers("search_reranker", DEFAULT_RERANK_MODEL);
242
247
  const recordInput = fromLayers("record_input", true);
243
248
  const keepRunsForDays = fromLayers("keep_runs_for_days", DEFAULT_KEEP_RUNS_FOR_DAYS);
249
+ const keepSuccessfulRunsForDays = fromLayers("keep_successful_runs_for_days", KEEP_SUCCESSFUL_RUNS_FOR_DAYS);
250
+ const runsSizeLimitMb = fromLayers("runs_size_limit_mb", RUNS_SIZE_LIMIT_MB);
244
251
  // Set where an agent runs unattended; the config file and `--permission` cannot undo it.
245
252
  const readOnlyLock = readOnlyLockOf(app, env);
246
253
  // A bot has no hourly limit unless the bot section gives it one: a bot that answers people is
@@ -297,8 +304,10 @@ export const settingsFor = (app, extension = {}) => {
297
304
  : (extension.parseDuration ?? parseDuration)(timeout.value, timeout.from === TIMEOUT ? TIMEOUT : "--timeout"),
298
305
  record: record.value,
299
306
  recordInput: recordInput.value,
300
- keepFailedRuns: record.value || record.from === "default",
307
+ keepFailedRuns: record.value || record.from !== "flag",
301
308
  keepRunsForDays: keepRunsForDays.value,
309
+ keepSuccessfulRunsForDays: keepSuccessfulRunsForDays.value,
310
+ runsSizeLimitMb: runsSizeLimitMb.value,
302
311
  ...(readOnlyLock ? { readOnlyLock } : {}),
303
312
  permissions: permissions.levels,
304
313
  permissionSources: permissions.sources,
@@ -326,6 +335,8 @@ export const settingsFor = (app, extension = {}) => {
326
335
  record: record.from,
327
336
  recordInput: recordInput.from,
328
337
  keepRunsForDays: keepRunsForDays.from,
338
+ keepSuccessfulRunsForDays: keepSuccessfulRunsForDays.from,
339
+ runsSizeLimitMb: runsSizeLimitMb.from,
329
340
  sendsPerHour: sendsPerHour.from,
330
341
  requestsPerMinute: requestsPerMinute.from,
331
342
  readOtherBots: readOtherBots.from,
@@ -79,8 +79,10 @@ export declare const configCommand: (app: import("./entry.js").AppIdentity, conf
79
79
  color: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
80
80
  sender_colors: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
81
81
  record: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
82
- record_input: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
82
+ record_input: import("valibot").OptionalSchema<import("valibot").UnionSchema<[import("valibot").BooleanSchema<undefined>, import("valibot").LiteralSchema<"failed", undefined>], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
83
83
  keep_runs_for_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
84
+ keep_successful_runs_for_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
85
+ runs_size_limit_mb: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
84
86
  permissions: import("valibot").OptionalSchema<import("valibot").RecordSchema<import("valibot").GenericSchema<string>, import("valibot").PicklistSchema<readonly ["deny", "readonly", "ask", "allow"], (issue: import("valibot").PicklistIssue) => string>, (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
85
87
  sends_per_hour: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
86
88
  requests_per_minute: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 0, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
@@ -153,8 +155,10 @@ export declare const configCommand: (app: import("./entry.js").AppIdentity, conf
153
155
  color: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
154
156
  sender_colors: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
155
157
  record: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
156
- record_input: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
158
+ record_input: import("valibot").OptionalSchema<import("valibot").UnionSchema<[import("valibot").BooleanSchema<undefined>, import("valibot").LiteralSchema<"failed", undefined>], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
157
159
  keep_runs_for_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
160
+ keep_successful_runs_for_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
161
+ runs_size_limit_mb: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
158
162
  permissions: import("valibot").OptionalSchema<import("valibot").RecordSchema<import("valibot").GenericSchema<string>, import("valibot").PicklistSchema<readonly ["deny", "readonly", "ask", "allow"], (issue: import("valibot").PicklistIssue) => string>, (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
159
163
  sends_per_hour: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
160
164
  requests_per_minute: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 0, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
@@ -283,8 +287,10 @@ export declare const completeCommand: (messenger: import("./index.js").Messenger
283
287
  color: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
284
288
  sender_colors: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
285
289
  record: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
286
- record_input: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
290
+ record_input: import("valibot").OptionalSchema<import("valibot").UnionSchema<[import("valibot").BooleanSchema<undefined>, import("valibot").LiteralSchema<"failed", undefined>], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
287
291
  keep_runs_for_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
292
+ keep_successful_runs_for_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
293
+ runs_size_limit_mb: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
288
294
  permissions: import("valibot").OptionalSchema<import("valibot").RecordSchema<import("valibot").GenericSchema<string>, import("valibot").PicklistSchema<readonly ["deny", "readonly", "ask", "allow"], (issue: import("valibot").PicklistIssue) => string>, (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
289
295
  sends_per_hour: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
290
296
  requests_per_minute: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 0, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
@@ -357,8 +363,10 @@ export declare const completeCommand: (messenger: import("./index.js").Messenger
357
363
  color: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
358
364
  sender_colors: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
359
365
  record: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
360
- record_input: import("valibot").OptionalSchema<import("valibot").BooleanSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
366
+ record_input: import("valibot").OptionalSchema<import("valibot").UnionSchema<[import("valibot").BooleanSchema<undefined>, import("valibot").LiteralSchema<"failed", undefined>], (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
361
367
  keep_runs_for_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
368
+ keep_successful_runs_for_days: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
369
+ runs_size_limit_mb: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
362
370
  permissions: import("valibot").OptionalSchema<import("valibot").RecordSchema<import("valibot").GenericSchema<string>, import("valibot").PicklistSchema<readonly ["deny", "readonly", "ask", "allow"], (issue: import("valibot").PicklistIssue) => string>, (issue: import("valibot").BaseIssue<unknown>) => string>, undefined>;
363
371
  sends_per_hour: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 1, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
364
372
  requests_per_minute: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<readonly [import("valibot").NumberSchema<(issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").IntegerAction<number, (issue: import("valibot").BaseIssue<unknown>) => string>, import("valibot").MinValueAction<number, 0, (issue: import("valibot").BaseIssue<unknown>) => string>]>, undefined>;
@@ -4,4 +4,6 @@ export declare const accountProfile: v.StrictObjectSchema<{
4
4
  readonly current: v.BooleanSchema<undefined>;
5
5
  readonly account: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
6
6
  readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
7
+ /** False for a profile with no session on this computer; absent when the messenger does not say. */
8
+ readonly signed_in: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
7
9
  }, undefined>;
@@ -5,4 +5,6 @@ export const accountProfile = v.strictObject({
5
5
  current: v.boolean(),
6
6
  account: v.nullable(id),
7
7
  name: v.nullable(text),
8
+ /** False for a profile with no session on this computer; absent when the messenger does not say. */
9
+ signed_in: v.optional(v.boolean()),
8
10
  });
@@ -4177,6 +4177,7 @@ export declare const ENTITIES: {
4177
4177
  readonly current: v.BooleanSchema<undefined>;
4178
4178
  readonly account: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
4179
4179
  readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
4180
+ readonly signed_in: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
4180
4181
  }, undefined>;
4181
4182
  readonly organization: v.StrictObjectSchema<{
4182
4183
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
@@ -6311,7 +6312,20 @@ export declare const ENTITIES: {
6311
6312
  readonly kind: v.StringSchema<undefined>;
6312
6313
  readonly session_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
6313
6314
  readonly from_chat: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
6315
+ readonly client: v.OptionalSchema<v.StrictObjectSchema<{
6316
+ readonly name: v.StringSchema<undefined>;
6317
+ readonly version: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6318
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6319
+ }, undefined>, undefined>;
6314
6320
  }, undefined>, undefined>;
6321
+ readonly via: v.OptionalSchema<v.PicklistSchema<["cli", "mcp-stdio", "mcp-http", "desktop"], undefined>, undefined>;
6322
+ readonly mcp: v.OptionalSchema<v.StrictObjectSchema<{
6323
+ readonly tool: v.StringSchema<undefined>;
6324
+ readonly request_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6325
+ readonly connection_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6326
+ readonly proxied: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
6327
+ }, undefined>, undefined>;
6328
+ readonly events_dropped: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
6315
6329
  readonly error: v.OptionalSchema<v.StrictObjectSchema<{
6316
6330
  readonly class: v.StringSchema<undefined>;
6317
6331
  readonly code: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
@@ -6374,7 +6388,20 @@ export declare const ENTITIES: {
6374
6388
  readonly kind: v.StringSchema<undefined>;
6375
6389
  readonly session_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
6376
6390
  readonly from_chat: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
6391
+ readonly client: v.OptionalSchema<v.StrictObjectSchema<{
6392
+ readonly name: v.StringSchema<undefined>;
6393
+ readonly version: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6394
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6395
+ }, undefined>, undefined>;
6396
+ }, undefined>, undefined>;
6397
+ readonly via: v.OptionalSchema<v.PicklistSchema<["cli", "mcp-stdio", "mcp-http", "desktop"], undefined>, undefined>;
6398
+ readonly mcp: v.OptionalSchema<v.StrictObjectSchema<{
6399
+ readonly tool: v.StringSchema<undefined>;
6400
+ readonly request_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6401
+ readonly connection_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6402
+ readonly proxied: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
6377
6403
  }, undefined>, undefined>;
6404
+ readonly events_dropped: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
6378
6405
  readonly error: v.OptionalSchema<v.StrictObjectSchema<{
6379
6406
  readonly class: v.StringSchema<undefined>;
6380
6407
  readonly code: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
@@ -6436,7 +6463,20 @@ export declare const ENTITIES: {
6436
6463
  readonly kind: v.StringSchema<undefined>;
6437
6464
  readonly session_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
6438
6465
  readonly from_chat: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
6466
+ readonly client: v.OptionalSchema<v.StrictObjectSchema<{
6467
+ readonly name: v.StringSchema<undefined>;
6468
+ readonly version: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6469
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6470
+ }, undefined>, undefined>;
6471
+ }, undefined>, undefined>;
6472
+ readonly via: v.OptionalSchema<v.PicklistSchema<["cli", "mcp-stdio", "mcp-http", "desktop"], undefined>, undefined>;
6473
+ readonly mcp: v.OptionalSchema<v.StrictObjectSchema<{
6474
+ readonly tool: v.StringSchema<undefined>;
6475
+ readonly request_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6476
+ readonly connection_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
6477
+ readonly proxied: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
6439
6478
  }, undefined>, undefined>;
6479
+ readonly events_dropped: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
6440
6480
  readonly error: v.OptionalSchema<v.StrictObjectSchema<{
6441
6481
  readonly class: v.StringSchema<undefined>;
6442
6482
  readonly code: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
@@ -35,7 +35,20 @@ export declare const recordedRun: v.StrictObjectSchema<{
35
35
  readonly kind: v.StringSchema<undefined>;
36
36
  readonly session_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
37
37
  readonly from_chat: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
38
+ readonly client: v.OptionalSchema<v.StrictObjectSchema<{
39
+ readonly name: v.StringSchema<undefined>;
40
+ readonly version: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
41
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
42
+ }, undefined>, undefined>;
43
+ }, undefined>, undefined>;
44
+ readonly via: v.OptionalSchema<v.PicklistSchema<["cli", "mcp-stdio", "mcp-http", "desktop"], undefined>, undefined>;
45
+ readonly mcp: v.OptionalSchema<v.StrictObjectSchema<{
46
+ readonly tool: v.StringSchema<undefined>;
47
+ readonly request_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
48
+ readonly connection_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
49
+ readonly proxied: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
38
50
  }, undefined>, undefined>;
51
+ readonly events_dropped: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
39
52
  readonly error: v.OptionalSchema<v.StrictObjectSchema<{
40
53
  readonly class: v.StringSchema<undefined>;
41
54
  readonly code: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
@@ -98,7 +111,20 @@ export declare const runDetails: v.StrictObjectSchema<{
98
111
  readonly kind: v.StringSchema<undefined>;
99
112
  readonly session_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
100
113
  readonly from_chat: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
114
+ readonly client: v.OptionalSchema<v.StrictObjectSchema<{
115
+ readonly name: v.StringSchema<undefined>;
116
+ readonly version: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
117
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
118
+ }, undefined>, undefined>;
101
119
  }, undefined>, undefined>;
120
+ readonly via: v.OptionalSchema<v.PicklistSchema<["cli", "mcp-stdio", "mcp-http", "desktop"], undefined>, undefined>;
121
+ readonly mcp: v.OptionalSchema<v.StrictObjectSchema<{
122
+ readonly tool: v.StringSchema<undefined>;
123
+ readonly request_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
124
+ readonly connection_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
125
+ readonly proxied: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
126
+ }, undefined>, undefined>;
127
+ readonly events_dropped: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
102
128
  readonly error: v.OptionalSchema<v.StrictObjectSchema<{
103
129
  readonly class: v.StringSchema<undefined>;
104
130
  readonly code: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
@@ -160,7 +186,20 @@ export declare const runHit: v.StrictObjectSchema<{
160
186
  readonly kind: v.StringSchema<undefined>;
161
187
  readonly session_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
162
188
  readonly from_chat: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
189
+ readonly client: v.OptionalSchema<v.StrictObjectSchema<{
190
+ readonly name: v.StringSchema<undefined>;
191
+ readonly version: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
192
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
193
+ }, undefined>, undefined>;
194
+ }, undefined>, undefined>;
195
+ readonly via: v.OptionalSchema<v.PicklistSchema<["cli", "mcp-stdio", "mcp-http", "desktop"], undefined>, undefined>;
196
+ readonly mcp: v.OptionalSchema<v.StrictObjectSchema<{
197
+ readonly tool: v.StringSchema<undefined>;
198
+ readonly request_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
199
+ readonly connection_id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
200
+ readonly proxied: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
163
201
  }, undefined>, undefined>;
202
+ readonly events_dropped: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
164
203
  readonly error: v.OptionalSchema<v.StrictObjectSchema<{
165
204
  readonly class: v.StringSchema<undefined>;
166
205
  readonly code: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
@@ -28,7 +28,20 @@ export const recordedRun = v.strictObject({
28
28
  cli_command: v.optional(text),
29
29
  argv: v.optional(v.array(text)),
30
30
  unknown_command: v.optional(text),
31
- agent: v.optional(v.strictObject({ kind: text, session_id: v.nullable(text), from_chat: v.optional(v.literal(true)) })),
31
+ agent: v.optional(v.strictObject({
32
+ kind: text,
33
+ session_id: v.nullable(text),
34
+ from_chat: v.optional(v.literal(true)),
35
+ client: v.optional(v.strictObject({ name: text, version: v.optional(text), id: v.optional(text) })),
36
+ })),
37
+ via: v.optional(v.picklist(["cli", "mcp-stdio", "mcp-http", "desktop"])),
38
+ mcp: v.optional(v.strictObject({
39
+ tool: text,
40
+ request_id: v.optional(text),
41
+ connection_id: v.optional(text),
42
+ proxied: v.optional(v.literal(true)),
43
+ })),
44
+ events_dropped: v.optional(count),
32
45
  error: v.optional(v.strictObject({
33
46
  class: text,
34
47
  code: v.optional(text),
@@ -7,6 +7,7 @@ export type { CommandAvailability } from "../cli/command-availability.js";
7
7
  import type { RunOptions } from "../cli/program.js";
8
8
  import type { StdinField } from "../cli/stdin-field.js";
9
9
  import type { MessengerAdapter } from "../ports/messenger.js";
10
+ import type { RunCaller } from "../runs/run-context.js";
10
11
  import { type Level, type PermissionKey } from "../sends/permissions.js";
11
12
  /** The CLI's own `run` — a fresh program on every call — and its command tree as `commands --json` describes it. */
12
13
  export interface AgentProgram {
@@ -32,6 +33,8 @@ export interface AgentContext {
32
33
  stdin?: (path: readonly string[]) => StdinField | undefined;
33
34
  /** More of the run's environment, read by the commands that look for it: the server's `spawnJob`, `embedders`. */
34
35
  environment?: Readonly<Record<string, unknown>>;
36
+ /** Who made the call (`callerOf`); the run records it rather than this server's environment. */
37
+ caller?: RunCaller;
35
38
  }
36
39
  export type AgentAnswer = {
37
40
  result: unknown;
package/dist/mcp/agent.js CHANGED
@@ -8,6 +8,7 @@ import { commandAvailability } from "../cli/command-availability.js";
8
8
  import { changesMessenger } from "../cli/effects.js";
9
9
  import { lentConnection } from "../cli/messenger/serve-relay.js";
10
10
  import { authorityAllowed, botApiAllowed } from "../sends/permissions.js";
11
+ import { currentCaller } from "./caller.js";
11
12
  const TYPES = new Map(VOCABULARY.flags.map((flag) => [flag.name, flag.type]));
12
13
  // `<name|id>` is one argument: its field is `name_or_id`.
13
14
  const snake = (name) => name.replaceAll("|", "_or_").replace(/[^a-z0-9]+/gi, "_");
@@ -231,6 +232,7 @@ export const runCommandForAgent = async (program, path, input, ctx) => {
231
232
  ...(ctx.overrides?.length ? { overrides: ctx.overrides } : {}),
232
233
  authority: levels !== undefined && authorityAllowed(levels),
233
234
  botApi: levels !== undefined && botApiAllowed(levels),
235
+ ...((ctx.caller ?? currentCaller()) ? { caller: ctx.caller ?? currentCaller() } : {}),
234
236
  },
235
237
  // Opened only when the command asks: a read the store answers never connects.
236
238
  ...(lend
@@ -16,14 +16,24 @@ export interface AllProfilesOptions {
16
16
  name: string;
17
17
  /** Read again for each new session, so a profile set up while serving over HTTP shows up. */
18
18
  profiles: () => readonly string[];
19
+ /** Whether a profile has a session now; unset, every one does. A call for one without is refused with `signIn`. */
20
+ signedIn?: (profile: string) => boolean;
21
+ /** How to sign a profile in, for the refusal of a call it cannot serve yet. */
22
+ signIn?: (profile: string) => string;
19
23
  child: (profile: string) => ChildSpec;
20
24
  instructions: string;
21
25
  idleMs?: number;
22
26
  /** A child's stderr, line by line; never this process's stdout, which carries the protocol. */
23
27
  note?: (line: string) => void;
28
+ /** This server's environment, which names the agent that started it on stdio; `process.env` when left out. */
29
+ env?: NodeJS.ProcessEnv;
24
30
  }
25
- /** The child's tool with the one field that picks the profile, required and without a default. */
26
- export declare const withProfile: (tool: Tool, profiles: readonly string[], name: string) => Tool;
31
+ /**
32
+ * The child's tool with the one field that picks the profile, required and without a default. Each profile carries
33
+ * its state as the title an app shows in its menu (`oneOf` with `const` and `title`, as MCP's titled enums), and the
34
+ * description repeats it for an app that shows only the description.
35
+ */
36
+ export declare const withProfile: (tool: Tool, profiles: readonly string[], name: string, signedIn?: (profile: string) => boolean) => Tool;
27
37
  /**
28
38
  * **One server for every profile of a tool** (TASK-26): `<tool> mcp` shows the tools `<tool> <profile> mcp` shows,
29
39
  * each with a required `profile`, and runs a call in that profile's own server, started as a child the first time
@@ -1,6 +1,8 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
2
3
  import { McpServer } from "@modelcontextprotocol/server";
3
4
  import { StdioServerTransport, serveStdio } from "@modelcontextprotocol/server/stdio";
5
+ import { CALLER_META_KEY, callerOf, PROXY_CLIENT_NAME } from "./caller.js";
4
6
  import { answeredWire } from "./stdio-wire.js";
5
7
  const PROTOCOL = "2025-06-18";
6
8
  const START_MS = 30_000;
@@ -82,7 +84,7 @@ const startChild = (spec, note, onExit) => {
82
84
  return request("initialize", {
83
85
  protocolVersion: PROTOCOL,
84
86
  capabilities: {},
85
- clientInfo: { name: "all-profiles", version: "1" },
87
+ clientInfo: { name: PROXY_CLIENT_NAME, version: "1" },
86
88
  })
87
89
  .then(() => {
88
90
  send({ method: "notifications/initialized" });
@@ -138,9 +140,17 @@ const pool = (options) => {
138
140
  };
139
141
  return { use, close };
140
142
  };
141
- /** The child's tool with the one field that picks the profile, required and without a default. */
142
- export const withProfile = (tool, profiles, name) => {
143
+ /**
144
+ * The child's tool with the one field that picks the profile, required and without a default. Each profile carries
145
+ * its state as the title an app shows in its menu (`oneOf` with `const` and `title`, as MCP's titled enums), and the
146
+ * description repeats it for an app that shows only the description.
147
+ */
148
+ export const withProfile = (tool, profiles, name, signedIn = () => true) => {
143
149
  const schema = tool.inputSchema;
150
+ const out = profiles.filter((profile) => !signedIn(profile));
151
+ const states = out.length === 0
152
+ ? ""
153
+ : ` Signed in: ${profiles.filter(signedIn).join(", ") || "none"} · Not signed in: ${out.join(", ")}.`;
144
154
  return {
145
155
  ...tool,
146
156
  inputSchema: {
@@ -149,7 +159,11 @@ export const withProfile = (tool, profiles, name) => {
149
159
  profile: {
150
160
  type: "string",
151
161
  enum: [...profiles],
152
- description: `Which ${name} account, by its profile. If the owner did not name one, ask which — never guess.`,
162
+ oneOf: profiles.map((profile) => ({
163
+ const: profile,
164
+ title: signedIn(profile) ? profile : `${profile} — not signed in`,
165
+ })),
166
+ description: `Which ${name} account, by its profile. If the owner did not name one, ask which — never guess.${states}`,
153
167
  },
154
168
  ...schema.properties,
155
169
  },
@@ -174,26 +188,42 @@ export const allProfilesServer = (options) => {
174
188
  // list comes from whichever answers first and is kept.
175
189
  const tools = (profiles) => {
176
190
  listed ??= children
177
- .use(profiles[0], (child) => child.request("tools/list", {}))
191
+ .use(profiles.find(signedIn) ?? profiles[0], (child) => child.request("tools/list", {}))
178
192
  .then((answer) => (answer.tools ?? []));
179
193
  listed.catch(() => {
180
194
  listed = undefined;
181
195
  });
182
196
  return listed;
183
197
  };
198
+ const signedIn = (profile) => options.signedIn?.(profile) ?? true;
184
199
  const build = () => {
185
200
  const profiles = [...options.profiles()].sort();
186
201
  const server = new McpServer({ name: options.command, version: options.version }, { capabilities: { tools: {} }, instructions: options.instructions });
187
202
  server.server.setRequestHandler("tools/list", async () => ({
188
- tools: profiles.length === 0 ? [] : (await tools(profiles)).map((one) => withProfile(one, profiles, options.name)),
203
+ tools: profiles.length === 0
204
+ ? []
205
+ : (await tools(profiles)).map((one) => withProfile(one, profiles, options.name, signedIn)),
189
206
  }));
190
- server.server.setRequestHandler("tools/call", async (request) => {
207
+ const callers = {
208
+ env: options.env ?? process.env,
209
+ client: () => server.server.getClientVersion(),
210
+ connectionId: randomUUID(),
211
+ };
212
+ server.server.setRequestHandler("tools/call", async (request, ctx) => {
191
213
  const { name, arguments: given = {}, _meta } = request.params;
192
214
  const { profile, ...rest } = given;
193
215
  if (typeof profile !== "string" || !profiles.includes(profile))
194
216
  return refusal(`${name} needs "profile", one of: ${profiles.join(", ")}. If the owner did not say which account, ask.`);
217
+ if (!signedIn(profile))
218
+ return refusal(options.signIn?.(profile) ?? `profile ${profile} is not signed in.`);
195
219
  try {
196
- return (await children.use(profile, (child) => child.request("tools/call", { name, arguments: rest, ...(_meta ? { _meta } : {}) })));
220
+ return (await children.use(profile, (child) =>
221
+ // Children are shared by every session, so each call names its own caller.
222
+ child.request("tools/call", {
223
+ name,
224
+ arguments: rest,
225
+ _meta: { ..._meta, [CALLER_META_KEY]: callerOf(ctx, name, callers) },
226
+ })));
197
227
  }
198
228
  catch (error) {
199
229
  return refusal(`profile ${profile}: ${error instanceof Error ? error.message : String(error)}`);