@wirecat/cli-core 0.466.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 (45) hide show
  1. package/CHANGELOG.md +36 -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 +2 -2
  8. package/dist/cli/index.js +1 -1
  9. package/dist/cli/messenger/context.js +1 -1
  10. package/dist/cli/messenger/observed.js +10 -2
  11. package/dist/cli/messenger/report.js +48 -3
  12. package/dist/cli/program.d.ts +1 -1
  13. package/dist/cli/program.js +7 -3
  14. package/dist/cli/settings.d.ts +19 -6
  15. package/dist/cli/settings.js +14 -3
  16. package/dist/cli/shared-command-exports.d.ts +12 -4
  17. package/dist/entities/index.d.ts +39 -0
  18. package/dist/entities/recorded-run.d.ts +39 -0
  19. package/dist/entities/recorded-run.js +14 -1
  20. package/dist/mcp/agent.d.ts +3 -0
  21. package/dist/mcp/agent.js +2 -0
  22. package/dist/mcp/all-profiles.d.ts +2 -0
  23. package/dist/mcp/all-profiles.js +16 -3
  24. package/dist/mcp/bot/server.js +8 -3
  25. package/dist/mcp/caller.d.ts +38 -0
  26. package/dist/mcp/caller.js +95 -0
  27. package/dist/mcp/http/oauth.js +5 -1
  28. package/dist/mcp/server.js +19 -7
  29. package/dist/mcp/surface.js +11 -2
  30. package/dist/mcp/tool.d.ts +4 -1
  31. package/dist/mcp/tool.js +21 -11
  32. package/dist/runs/events.d.ts +16 -4
  33. package/dist/runs/events.js +4 -0
  34. package/dist/runs/log-attributes.js +7 -1
  35. package/dist/runs/log-events.js +5 -0
  36. package/dist/runs/recording.d.ts +10 -3
  37. package/dist/runs/recording.js +59 -15
  38. package/dist/runs/run-context.d.ts +55 -4
  39. package/dist/runs/run-context.js +34 -3
  40. package/dist/runs/run.d.ts +37 -11
  41. package/dist/runs/run.js +201 -52
  42. package/dist/runs/scope.d.ts +12 -0
  43. package/dist/runs/scope.js +6 -0
  44. package/dist/services/patience.js +5 -1
  45. package/package.json +1 -1
@@ -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>;
@@ -6312,7 +6312,20 @@ export declare const ENTITIES: {
6312
6312
  readonly kind: v.StringSchema<undefined>;
6313
6313
  readonly session_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
6314
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>;
6315
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>;
6316
6329
  readonly error: v.OptionalSchema<v.StrictObjectSchema<{
6317
6330
  readonly class: v.StringSchema<undefined>;
6318
6331
  readonly code: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
@@ -6375,7 +6388,20 @@ export declare const ENTITIES: {
6375
6388
  readonly kind: v.StringSchema<undefined>;
6376
6389
  readonly session_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
6377
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>;
6378
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>;
6379
6405
  readonly error: v.OptionalSchema<v.StrictObjectSchema<{
6380
6406
  readonly class: v.StringSchema<undefined>;
6381
6407
  readonly code: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
@@ -6437,7 +6463,20 @@ export declare const ENTITIES: {
6437
6463
  readonly kind: v.StringSchema<undefined>;
6438
6464
  readonly session_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
6439
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>;
6440
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>;
6441
6480
  readonly error: v.OptionalSchema<v.StrictObjectSchema<{
6442
6481
  readonly class: v.StringSchema<undefined>;
6443
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
@@ -25,6 +25,8 @@ export interface AllProfilesOptions {
25
25
  idleMs?: number;
26
26
  /** A child's stderr, line by line; never this process's stdout, which carries the protocol. */
27
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;
28
30
  }
29
31
  /**
30
32
  * The child's tool with the one field that picks the profile, required and without a default. Each profile carries
@@ -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" });
@@ -202,7 +204,12 @@ export const allProfilesServer = (options) => {
202
204
  ? []
203
205
  : (await tools(profiles)).map((one) => withProfile(one, profiles, options.name, signedIn)),
204
206
  }));
205
- 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) => {
206
213
  const { name, arguments: given = {}, _meta } = request.params;
207
214
  const { profile, ...rest } = given;
208
215
  if (typeof profile !== "string" || !profiles.includes(profile))
@@ -210,7 +217,13 @@ export const allProfilesServer = (options) => {
210
217
  if (!signedIn(profile))
211
218
  return refusal(options.signIn?.(profile) ?? `profile ${profile} is not signed in.`);
212
219
  try {
213
- 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
+ })));
214
227
  }
215
228
  catch (error) {
216
229
  return refusal(`profile ${profile}: ${error instanceof Error ? error.message : String(error)}`);
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { McpServer } from "@modelcontextprotocol/server";
2
3
  import { serveStdio } from "@modelcontextprotocol/server/stdio";
3
4
  import { mcpErrorResult } from "@wirecat/cli-base";
@@ -9,6 +10,7 @@ import { itemOf } from "../../cli/envelope.js";
9
10
  import { wholeList } from "../../cli/messenger/chat-answers.js";
10
11
  import { MESSENGER_CHANGE_REFUSAL, readOnlyLockOf, readOnlyRefusal } from "../../ports/app.js";
11
12
  import { runCommandForAgent, toolsFromCommands } from "../agent.js";
13
+ import { callerOf } from "../caller.js";
12
14
  import { normal, registerCommands } from "../surface.js";
13
15
  import { agentJson } from "../text.js";
14
16
  import { answered } from "../tool.js";
@@ -48,7 +50,7 @@ export const createBotServer = (options) => {
48
50
  const skill = options.skill ? skillResource(bot.app, options.skill) : undefined;
49
51
  // One at a time: the commands share the journal, the registry and the local copy.
50
52
  let queue = Promise.resolve();
51
- const invoke = (path, input) => {
53
+ const invoke = (path, input, caller) => {
52
54
  const next = queue.then(() => runCommandForAgent(program, path, path[0] === "config" ? { ...input, bot: true } : input, {
53
55
  profile: settings.profile,
54
56
  readOnly: readOnlyLockOf(bot.app, env) !== undefined,
@@ -56,11 +58,13 @@ export const createBotServer = (options) => {
56
58
  levels,
57
59
  ...(options.keyOf ? { keyOf: options.keyOf } : {}),
58
60
  authority,
61
+ ...(caller ? { caller } : {}),
59
62
  }));
60
63
  queue = next.catch(() => undefined);
61
64
  return next;
62
65
  };
63
66
  const build = () => {
67
+ const callers = { env, client: () => server.server.getClientVersion(), connectionId: randomUUID() };
64
68
  const server = new McpServer({ name: `${command}-bot-${settings.profile}`, version: bot.app.version }, {
65
69
  instructions: botInstructions({
66
70
  command,
@@ -82,8 +86,9 @@ export const createBotServer = (options) => {
82
86
  writes: tool.writes,
83
87
  availability: tool.availability,
84
88
  input: tool.input,
85
- run: async (args) => {
86
- const answer = await invoke(tool.path, args);
89
+ run: async (args, ctx) => {
90
+ const caller = callerOf(ctx, `${command}_${tool.command.replaceAll(" ", "_")}`, callers);
91
+ const answer = await invoke(tool.path, args, caller);
87
92
  return "error" in answer
88
93
  ? mcpErrorResult(JSON.parse(agentJson(answer.error)))
89
94
  : answered(body(tool.command, answer.result));
@@ -0,0 +1,38 @@
1
+ import { type RunCaller } from "../runs/run-context.js";
2
+ export { currentCaller, withCaller } from "../runs/run-context.js";
3
+ /** The `_meta` key the every-profile server names the real caller under, for the profile server it passes a call to. */
4
+ export declare const CALLER_META_KEY = "wirecat.dev/caller";
5
+ /** The name the every-profile server gives itself in `initialize`; only a connection from it may name a caller. */
6
+ export declare const PROXY_CLIENT_NAME = "all-profiles";
7
+ /** What a tool call's handler is given, reduced to what identifies the caller. */
8
+ export interface CallContext {
9
+ sessionId?: string | undefined;
10
+ mcpReq?: {
11
+ id?: string | number;
12
+ _meta?: Record<string, unknown> | undefined;
13
+ } | undefined;
14
+ http?: {
15
+ authInfo?: {
16
+ clientId: string;
17
+ extra?: Record<string, unknown> | undefined;
18
+ } | undefined;
19
+ } | undefined;
20
+ }
21
+ export interface CallerSource {
22
+ /** This server's environment: the coding agent that started a stdio server. Never read for an HTTP call. */
23
+ env: NodeJS.ProcessEnv;
24
+ /** The client's `initialize`, when this connection had one. */
25
+ client?: () => {
26
+ name: string;
27
+ version?: string;
28
+ } | undefined;
29
+ /** One id per stdio connection, made when the server starts. */
30
+ connectionId?: string;
31
+ }
32
+ /**
33
+ * Who made one MCP call. Over HTTP the server's own environment is somebody else's — whoever started `mcp --http` —
34
+ * so the caller is the OAuth client (its registered name and a hash of its id) and the HTTP session, if any. Over
35
+ * stdio the agent that started the server is the caller, and `initialize` names its client. A call the every-profile
36
+ * server passed on carries the caller it worked out, trusted only on a connection that server opened.
37
+ */
38
+ export declare const callerOf: (ctx: CallContext | undefined, tool: string, source: CallerSource) => RunCaller;
@@ -0,0 +1,95 @@
1
+ import { CLIENT_INFO_META_KEY } from "@modelcontextprotocol/server";
2
+ import { agentOf, clientHash } from "../runs/run-context.js";
3
+ export { currentCaller, withCaller } from "../runs/run-context.js";
4
+ /** The `_meta` key the every-profile server names the real caller under, for the profile server it passes a call to. */
5
+ export const CALLER_META_KEY = "wirecat.dev/caller";
6
+ /** The name the every-profile server gives itself in `initialize`; only a connection from it may name a caller. */
7
+ export const PROXY_CLIENT_NAME = "all-profiles";
8
+ const NAME = /^[\w .@()/+-]{1,64}$/;
9
+ const plain = (value) => typeof value === "string" && NAME.test(value.trim()) ? value.trim() : undefined;
10
+ const clientFrom = (value) => {
11
+ if (!value || typeof value !== "object")
12
+ return undefined;
13
+ const { name, version } = value;
14
+ const named = plain(name);
15
+ if (!named)
16
+ return undefined;
17
+ const versioned = plain(version);
18
+ return { name: named, ...(versioned ? { version: versioned } : {}) };
19
+ };
20
+ /**
21
+ * Who made one MCP call. Over HTTP the server's own environment is somebody else's — whoever started `mcp --http` —
22
+ * so the caller is the OAuth client (its registered name and a hash of its id) and the HTTP session, if any. Over
23
+ * stdio the agent that started the server is the caller, and `initialize` names its client. A call the every-profile
24
+ * server passed on carries the caller it worked out, trusted only on a connection that server opened.
25
+ */
26
+ export const callerOf = (ctx, tool, source) => {
27
+ const meta = ctx?.mcpReq?._meta;
28
+ const requestId = ctx?.mcpReq?.id;
29
+ const initialized = source.client?.();
30
+ const proxied = initialized?.name === PROXY_CLIENT_NAME ? passedOn(meta?.[CALLER_META_KEY], tool) : undefined;
31
+ if (proxied)
32
+ return proxied;
33
+ const client = clientFrom(meta?.[CLIENT_INFO_META_KEY]) ?? clientFrom(initialized);
34
+ const mcp = (connection) => ({
35
+ tool,
36
+ ...(requestId === undefined ? {} : { request_id: String(requestId) }),
37
+ ...(connection ? { connection_id: connection } : {}),
38
+ });
39
+ const auth = ctx?.http?.authInfo;
40
+ if (auth) {
41
+ const registered = plain(auth.extra?.clientName);
42
+ return {
43
+ via: "mcp-http",
44
+ agent: {
45
+ kind: "mcp-client",
46
+ session_id: ctx?.sessionId ?? null,
47
+ client: {
48
+ name: registered ?? client?.name ?? "unknown",
49
+ ...(client?.version ? { version: client.version } : {}),
50
+ id: clientHash(auth.clientId),
51
+ },
52
+ },
53
+ mcp: mcp(ctx?.sessionId),
54
+ };
55
+ }
56
+ const agent = agentOf(source.env) ?? { kind: "mcp-client", session_id: null };
57
+ return { via: "mcp-stdio", agent: { ...agent, ...(client ? { client } : {}) }, mcp: mcp(source.connectionId) };
58
+ };
59
+ /** The caller the every-profile server named, field by field: nothing else it sent reaches a run. */
60
+ const passedOn = (value, tool) => {
61
+ if (!value || typeof value !== "object")
62
+ return undefined;
63
+ const { via, agent, mcp } = value;
64
+ if (via !== "mcp-stdio" && via !== "mcp-http")
65
+ return undefined;
66
+ const a = agent && typeof agent === "object" ? agent : undefined;
67
+ const kind = plain(a?.kind);
68
+ const session = typeof a?.session_id === "string" && /^[A-Za-z0-9._:-]{1,128}$/.test(a.session_id) ? a.session_id : null;
69
+ const client = clientFrom(a?.client);
70
+ const id = a?.client && typeof a.client === "object" ? a.client.id : undefined;
71
+ const m = mcp && typeof mcp === "object" ? mcp : {};
72
+ const request = plain(m.request_id);
73
+ const connection = typeof m.connection_id === "string" && /^[\w-]{1,128}$/.test(m.connection_id) ? m.connection_id : undefined;
74
+ return {
75
+ via,
76
+ ...(kind
77
+ ? {
78
+ agent: {
79
+ kind,
80
+ session_id: session,
81
+ ...(a?.from_chat === true ? { from_chat: true } : {}),
82
+ ...(client
83
+ ? { client: { ...client, ...(typeof id === "string" && /^[0-9a-f]{8}$/.test(id) ? { id } : {}) } }
84
+ : {}),
85
+ },
86
+ }
87
+ : {}),
88
+ mcp: {
89
+ tool,
90
+ ...(request ? { request_id: request } : {}),
91
+ ...(connection ? { connection_id: connection } : {}),
92
+ proxied: true,
93
+ },
94
+ };
95
+ };
@@ -258,15 +258,19 @@ export const ownerLogin = ({ issuer, resource, file, appName, onCode, now = () =
258
258
  const verifier = {
259
259
  verifyAccessToken: async (presented) => {
260
260
  const seconds = Math.floor(now() / 1000);
261
- const found = store.read().tokens.find((one) => one.kind === "access" && one.hash === hash(presented));
261
+ const stored = store.read();
262
+ const found = stored.tokens.find((one) => one.kind === "access" && one.hash === hash(presented));
262
263
  if (!found || found.expiresAt <= seconds)
263
264
  throw new OAuthError(OAuthErrorCode.InvalidToken, "the token is unknown or expired");
265
+ // The name the app registered with, so a run can say which app called; the id itself never leaves here unhashed.
266
+ const clientName = stored.clients.find((one) => one.id === found.clientId)?.name;
264
267
  return {
265
268
  token: presented,
266
269
  clientId: found.clientId,
267
270
  scopes: [],
268
271
  expiresAt: found.expiresAt,
269
272
  resource,
273
+ ...(clientName ? { extra: { clientName } } : {}),
270
274
  };
271
275
  },
272
276
  };