@ai-sdk/harness-claude-code 1.0.66 → 1.0.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/bridge/index.mjs +27 -4
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +24669 -397
- package/dist/index.js +359 -41
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bridge/claude-code-system-prompt.ts +11 -0
- package/src/bridge/create-emit-stream-event.ts +18 -1
- package/src/bridge/index.ts +3 -1
- package/src/claude-code-bridge-protocol.ts +2 -0
- package/src/claude-code-harness.ts +397 -50
|
@@ -63,6 +63,11 @@ const CLAUDE_CODE_CLIENT_APP = `ai-sdk/harness-claude-code/${VERSION}`;
|
|
|
63
63
|
|
|
64
64
|
export type ClaudeCodeHarnessSettings = {
|
|
65
65
|
readonly auth?: ClaudeCodeAuthOptions;
|
|
66
|
+
/**
|
|
67
|
+
* MCP server definitions keyed by server name. Each definition uses the
|
|
68
|
+
* underlying runtime's native MCP server configuration format.
|
|
69
|
+
*/
|
|
70
|
+
readonly mcpServers?: Record<string, unknown>;
|
|
66
71
|
/**
|
|
67
72
|
* Anthropic model id the underlying `claude` CLI should use. Leaving this
|
|
68
73
|
* unset defers to the CLI's default.
|
|
@@ -92,19 +97,53 @@ export type ClaudeCodeHarnessSettings = {
|
|
|
92
97
|
readonly port?: number;
|
|
93
98
|
/** Maximum milliseconds to wait for the bridge to advertise its port. Defaults to 120000. */
|
|
94
99
|
readonly startupTimeoutMs?: number;
|
|
100
|
+
/**
|
|
101
|
+
* Creates the authentication token used by the sandbox bridge. Defaults to
|
|
102
|
+
* a random 32-byte hexadecimal token.
|
|
103
|
+
*/
|
|
104
|
+
readonly mintBridgeToken?: (sandboxId: string) => string;
|
|
95
105
|
};
|
|
96
106
|
|
|
97
107
|
/*
|
|
98
108
|
* Every native tool the Claude Code CLI can invoke, declared as a `ToolSet`
|
|
99
109
|
* keyed by what the bridge emits as `toolName` on the wire
|
|
100
|
-
* (`commonName ?? nativeName`). Schemas transcribed from
|
|
101
|
-
*
|
|
110
|
+
* (`commonName ?? nativeName`). Schemas are transcribed from the generated
|
|
111
|
+
* `sdk-tools.d.ts` and the conditionally registered tool definitions in the
|
|
112
|
+
* pinned Claude Code executable.
|
|
102
113
|
*
|
|
103
114
|
* `MCP` (the generic proxy tool inside the Claude Code SDK) is intentionally
|
|
104
115
|
* omitted — the bridge filters out `mcp__harness-tools__*` tool names before
|
|
105
116
|
* emitting them, and other MCP invocations come through with their own
|
|
106
117
|
* server-tool names rather than the literal `'Mcp'` token.
|
|
107
118
|
*/
|
|
119
|
+
const listMcpResourcesInputSchema = z.object({
|
|
120
|
+
server: z.string().optional(),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const readMcpResourceInputSchema = z.object({
|
|
124
|
+
server: z.string(),
|
|
125
|
+
uri: z.string(),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const structuredTeamMessageSchema = z.discriminatedUnion('type', [
|
|
129
|
+
z.object({
|
|
130
|
+
type: z.literal('shutdown_request'),
|
|
131
|
+
reason: z.string().optional(),
|
|
132
|
+
}),
|
|
133
|
+
z.object({
|
|
134
|
+
type: z.literal('shutdown_response'),
|
|
135
|
+
request_id: z.string(),
|
|
136
|
+
approve: z.boolean(),
|
|
137
|
+
reason: z.string().optional(),
|
|
138
|
+
}),
|
|
139
|
+
z.object({
|
|
140
|
+
type: z.literal('plan_approval_response'),
|
|
141
|
+
request_id: z.string(),
|
|
142
|
+
approve: z.boolean(),
|
|
143
|
+
feedback: z.string().optional(),
|
|
144
|
+
}),
|
|
145
|
+
]);
|
|
146
|
+
|
|
108
147
|
const CLAUDE_CODE_BUILTIN_TOOLS = {
|
|
109
148
|
read: commonTool('read', {
|
|
110
149
|
nativeName: 'Read',
|
|
@@ -296,23 +335,66 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
|
|
|
296
335
|
timeout: z.number(),
|
|
297
336
|
}),
|
|
298
337
|
}),
|
|
299
|
-
Monitor:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
338
|
+
Monitor: {
|
|
339
|
+
...tool({
|
|
340
|
+
description: 'Run and monitor a shell command or WebSocket',
|
|
341
|
+
inputSchema: z.object({
|
|
342
|
+
description: z.string().optional(),
|
|
343
|
+
timeout_ms: z.number().optional(),
|
|
344
|
+
persistent: z.boolean().optional(),
|
|
345
|
+
command: z.string().optional(),
|
|
346
|
+
ws: z
|
|
347
|
+
.object({
|
|
348
|
+
url: z.string(),
|
|
349
|
+
protocols: z.array(z.string()).optional(),
|
|
350
|
+
})
|
|
351
|
+
.optional(),
|
|
352
|
+
}),
|
|
306
353
|
}),
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
354
|
+
toolUseKind: 'bash',
|
|
355
|
+
},
|
|
356
|
+
ListMcpResources: {
|
|
357
|
+
...tool({
|
|
358
|
+
description: 'List resources available from MCP servers',
|
|
359
|
+
inputSchema: listMcpResourcesInputSchema,
|
|
360
|
+
}),
|
|
361
|
+
toolUseKind: 'readonly',
|
|
362
|
+
},
|
|
363
|
+
ListMcpResourcesTool: {
|
|
364
|
+
...tool({
|
|
365
|
+
description: 'List resources available from MCP servers',
|
|
366
|
+
inputSchema: listMcpResourcesInputSchema,
|
|
367
|
+
}),
|
|
368
|
+
toolUseKind: 'readonly',
|
|
369
|
+
},
|
|
370
|
+
ReadMcpResource: {
|
|
371
|
+
...tool({
|
|
372
|
+
description: 'Read a specific MCP resource by URI',
|
|
373
|
+
inputSchema: readMcpResourceInputSchema,
|
|
374
|
+
}),
|
|
375
|
+
toolUseKind: 'readonly',
|
|
376
|
+
},
|
|
377
|
+
ReadMcpResourceTool: {
|
|
378
|
+
...tool({
|
|
379
|
+
description: 'Read a specific MCP resource by URI',
|
|
380
|
+
inputSchema: readMcpResourceInputSchema,
|
|
381
|
+
}),
|
|
382
|
+
toolUseKind: 'readonly',
|
|
383
|
+
},
|
|
384
|
+
ReadMcpResourceDirTool: {
|
|
385
|
+
...tool({
|
|
386
|
+
description: 'List direct children of an MCP directory resource',
|
|
387
|
+
inputSchema: readMcpResourceInputSchema,
|
|
388
|
+
}),
|
|
389
|
+
toolUseKind: 'readonly',
|
|
390
|
+
},
|
|
391
|
+
RefreshMcpTools: {
|
|
392
|
+
...tool({
|
|
393
|
+
description: 'Refresh tools from one or all connected MCP servers',
|
|
394
|
+
inputSchema: z.object({ server: z.string().optional() }),
|
|
395
|
+
}),
|
|
396
|
+
toolUseKind: 'readonly',
|
|
397
|
+
},
|
|
316
398
|
ExitPlanMode: tool({
|
|
317
399
|
description: 'Exit plan mode with optional permission approvals',
|
|
318
400
|
inputSchema: z.looseObject({
|
|
@@ -326,6 +408,13 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
|
|
|
326
408
|
.optional(),
|
|
327
409
|
}),
|
|
328
410
|
}),
|
|
411
|
+
EnterPlanMode: {
|
|
412
|
+
...tool({
|
|
413
|
+
description: 'Enter plan mode',
|
|
414
|
+
inputSchema: z.object({}),
|
|
415
|
+
}),
|
|
416
|
+
toolUseKind: 'readonly',
|
|
417
|
+
},
|
|
329
418
|
EnterWorktree: tool({
|
|
330
419
|
description: 'Create or enter an isolated git worktree',
|
|
331
420
|
inputSchema: z.object({
|
|
@@ -391,6 +480,271 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
|
|
|
391
480
|
max_results: z.number().optional(),
|
|
392
481
|
}),
|
|
393
482
|
}),
|
|
483
|
+
Artifact: {
|
|
484
|
+
...tool({
|
|
485
|
+
description: 'Publish or list claude.ai artifacts',
|
|
486
|
+
inputSchema: z.object({
|
|
487
|
+
action: z.enum(['publish', 'list']).optional(),
|
|
488
|
+
file_path: z.string().optional(),
|
|
489
|
+
favicon: z.string().optional(),
|
|
490
|
+
limit: z.number().optional(),
|
|
491
|
+
scope: z.enum(['mine', 'shared', 'all']).optional(),
|
|
492
|
+
title: z.string().optional(),
|
|
493
|
+
description: z.string().optional(),
|
|
494
|
+
label: z.string().optional(),
|
|
495
|
+
url: z.string().optional(),
|
|
496
|
+
force: z.boolean().optional(),
|
|
497
|
+
}),
|
|
498
|
+
}),
|
|
499
|
+
toolUseKind: 'edit',
|
|
500
|
+
},
|
|
501
|
+
CronCreate: {
|
|
502
|
+
...tool({
|
|
503
|
+
description: 'Schedule a recurring or one-shot prompt',
|
|
504
|
+
inputSchema: z.object({
|
|
505
|
+
cron: z.string(),
|
|
506
|
+
prompt: z.string(),
|
|
507
|
+
recurring: z.boolean().optional(),
|
|
508
|
+
durable: z.boolean().optional(),
|
|
509
|
+
}),
|
|
510
|
+
}),
|
|
511
|
+
toolUseKind: 'edit',
|
|
512
|
+
},
|
|
513
|
+
CronDelete: {
|
|
514
|
+
...tool({
|
|
515
|
+
description: 'Delete a scheduled prompt by id',
|
|
516
|
+
inputSchema: z.object({ id: z.string() }),
|
|
517
|
+
}),
|
|
518
|
+
toolUseKind: 'edit',
|
|
519
|
+
},
|
|
520
|
+
CronList: {
|
|
521
|
+
...tool({
|
|
522
|
+
description: 'List scheduled prompts for the current session',
|
|
523
|
+
inputSchema: z.object({}),
|
|
524
|
+
}),
|
|
525
|
+
toolUseKind: 'readonly',
|
|
526
|
+
},
|
|
527
|
+
DesignSync: {
|
|
528
|
+
...tool({
|
|
529
|
+
description: 'Read or update claude.ai Design projects',
|
|
530
|
+
inputSchema: z.object({
|
|
531
|
+
method: z.enum([
|
|
532
|
+
'list_projects',
|
|
533
|
+
'get_project',
|
|
534
|
+
'list_files',
|
|
535
|
+
'get_file',
|
|
536
|
+
'finalize_plan',
|
|
537
|
+
'write_files',
|
|
538
|
+
'delete_files',
|
|
539
|
+
'register_assets',
|
|
540
|
+
'unregister_assets',
|
|
541
|
+
'create_project',
|
|
542
|
+
'report_validate',
|
|
543
|
+
]),
|
|
544
|
+
projectId: z.string().optional(),
|
|
545
|
+
path: z.string().optional(),
|
|
546
|
+
writes: z.array(z.string()).max(256).optional(),
|
|
547
|
+
deletes: z.array(z.string()).max(256).optional(),
|
|
548
|
+
planId: z.string().optional(),
|
|
549
|
+
files: z
|
|
550
|
+
.array(
|
|
551
|
+
z.object({
|
|
552
|
+
path: z.string(),
|
|
553
|
+
localPath: z.string().optional(),
|
|
554
|
+
data: z.string().optional(),
|
|
555
|
+
encoding: z.literal('base64').optional(),
|
|
556
|
+
mimeType: z.string().optional(),
|
|
557
|
+
}),
|
|
558
|
+
)
|
|
559
|
+
.max(256)
|
|
560
|
+
.optional(),
|
|
561
|
+
paths: z.array(z.string()).max(256).optional(),
|
|
562
|
+
name: z.string().optional(),
|
|
563
|
+
assets: z
|
|
564
|
+
.array(
|
|
565
|
+
z.object({
|
|
566
|
+
name: z.string(),
|
|
567
|
+
path: z.string(),
|
|
568
|
+
subtitle: z.string().optional(),
|
|
569
|
+
viewport: z
|
|
570
|
+
.object({
|
|
571
|
+
width: z.number(),
|
|
572
|
+
height: z.number().optional(),
|
|
573
|
+
})
|
|
574
|
+
.optional(),
|
|
575
|
+
group: z.string().optional(),
|
|
576
|
+
}),
|
|
577
|
+
)
|
|
578
|
+
.max(256)
|
|
579
|
+
.optional(),
|
|
580
|
+
localDir: z.string().optional(),
|
|
581
|
+
counts: z
|
|
582
|
+
.object({
|
|
583
|
+
total: z.number(),
|
|
584
|
+
bad: z.number(),
|
|
585
|
+
thin: z.number(),
|
|
586
|
+
variantsIdentical: z.number(),
|
|
587
|
+
iterations: z.number(),
|
|
588
|
+
})
|
|
589
|
+
.optional(),
|
|
590
|
+
}),
|
|
591
|
+
}),
|
|
592
|
+
toolUseKind: 'edit',
|
|
593
|
+
},
|
|
594
|
+
LSP: {
|
|
595
|
+
...tool({
|
|
596
|
+
description: 'Query a language server for code intelligence',
|
|
597
|
+
inputSchema: z.object({
|
|
598
|
+
operation: z.enum([
|
|
599
|
+
'goToDefinition',
|
|
600
|
+
'findReferences',
|
|
601
|
+
'hover',
|
|
602
|
+
'documentSymbol',
|
|
603
|
+
'workspaceSymbol',
|
|
604
|
+
'goToImplementation',
|
|
605
|
+
'prepareCallHierarchy',
|
|
606
|
+
'incomingCalls',
|
|
607
|
+
'outgoingCalls',
|
|
608
|
+
]),
|
|
609
|
+
filePath: z.string(),
|
|
610
|
+
line: z.number().int().positive(),
|
|
611
|
+
character: z.number().int().positive(),
|
|
612
|
+
query: z.string().optional(),
|
|
613
|
+
}),
|
|
614
|
+
}),
|
|
615
|
+
toolUseKind: 'readonly',
|
|
616
|
+
},
|
|
617
|
+
PowerShell: {
|
|
618
|
+
...tool({
|
|
619
|
+
description: 'Execute a PowerShell command, optionally in background',
|
|
620
|
+
inputSchema: z.object({
|
|
621
|
+
command: z.string(),
|
|
622
|
+
timeout: z.number().optional(),
|
|
623
|
+
description: z.string().optional(),
|
|
624
|
+
run_in_background: z.boolean().optional(),
|
|
625
|
+
dangerouslyDisableSandbox: z.boolean().optional(),
|
|
626
|
+
}),
|
|
627
|
+
}),
|
|
628
|
+
toolUseKind: 'bash',
|
|
629
|
+
},
|
|
630
|
+
PushNotification: {
|
|
631
|
+
...tool({
|
|
632
|
+
description: 'Send a notification for proactive or scheduled work',
|
|
633
|
+
inputSchema: z.object({
|
|
634
|
+
message: z.string(),
|
|
635
|
+
status: z.literal('proactive'),
|
|
636
|
+
}),
|
|
637
|
+
}),
|
|
638
|
+
toolUseKind: 'edit',
|
|
639
|
+
},
|
|
640
|
+
RemoteTrigger: {
|
|
641
|
+
...tool({
|
|
642
|
+
description: 'List, manage, or run a claude.ai Routine trigger',
|
|
643
|
+
inputSchema: z.object({
|
|
644
|
+
action: z.enum(['list', 'get', 'create', 'update', 'run']),
|
|
645
|
+
trigger_id: z.string().optional(),
|
|
646
|
+
body: z.record(z.string(), z.unknown()).optional(),
|
|
647
|
+
}),
|
|
648
|
+
}),
|
|
649
|
+
toolUseKind: 'edit',
|
|
650
|
+
},
|
|
651
|
+
ReportFindings: {
|
|
652
|
+
...tool({
|
|
653
|
+
description: 'Return verified code-review findings',
|
|
654
|
+
inputSchema: z.object({
|
|
655
|
+
level: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
|
|
656
|
+
findings: z
|
|
657
|
+
.array(
|
|
658
|
+
z.object({
|
|
659
|
+
file: z.string(),
|
|
660
|
+
line: z.number().optional(),
|
|
661
|
+
summary: z.string(),
|
|
662
|
+
short_summary: z.string().optional(),
|
|
663
|
+
failure_scenario: z.string(),
|
|
664
|
+
category: z.string().optional(),
|
|
665
|
+
verdict: z.enum(['CONFIRMED', 'PLAUSIBLE']).optional(),
|
|
666
|
+
outcome: z
|
|
667
|
+
.enum(['fixed', 'skipped', 'no_change_needed'])
|
|
668
|
+
.optional(),
|
|
669
|
+
}),
|
|
670
|
+
)
|
|
671
|
+
.max(32),
|
|
672
|
+
}),
|
|
673
|
+
}),
|
|
674
|
+
toolUseKind: 'readonly',
|
|
675
|
+
},
|
|
676
|
+
ScheduleWakeup: {
|
|
677
|
+
...tool({
|
|
678
|
+
description: 'Schedule or stop the next iteration of a dynamic loop',
|
|
679
|
+
inputSchema: z.object({
|
|
680
|
+
delaySeconds: z.number().optional(),
|
|
681
|
+
reason: z.string().optional(),
|
|
682
|
+
prompt: z.string().optional(),
|
|
683
|
+
stop: z.boolean().optional(),
|
|
684
|
+
}),
|
|
685
|
+
}),
|
|
686
|
+
toolUseKind: 'edit',
|
|
687
|
+
},
|
|
688
|
+
SendMessage: {
|
|
689
|
+
...tool({
|
|
690
|
+
description: 'Send a plain-text or protocol message to another agent',
|
|
691
|
+
inputSchema: z.object({
|
|
692
|
+
to: z.string(),
|
|
693
|
+
summary: z.string().max(200).optional(),
|
|
694
|
+
message: z.union([z.string(), structuredTeamMessageSchema]),
|
|
695
|
+
}),
|
|
696
|
+
}),
|
|
697
|
+
toolUseKind: 'edit',
|
|
698
|
+
},
|
|
699
|
+
SendUserFile: {
|
|
700
|
+
...tool({
|
|
701
|
+
description: 'Send one or more files to the user',
|
|
702
|
+
inputSchema: z.object({
|
|
703
|
+
files: z.union([z.string(), z.array(z.string()).min(1)]),
|
|
704
|
+
caption: z.string().optional(),
|
|
705
|
+
status: z.enum(['normal', 'proactive']),
|
|
706
|
+
display: z.enum(['render', 'attach']).optional(),
|
|
707
|
+
}),
|
|
708
|
+
}),
|
|
709
|
+
toolUseKind: 'readonly',
|
|
710
|
+
},
|
|
711
|
+
ShareOnboardingGuide: {
|
|
712
|
+
...tool({
|
|
713
|
+
description: 'Create, update, inspect, or delete an onboarding guide',
|
|
714
|
+
inputSchema: z.object({
|
|
715
|
+
mode: z.enum(['check', 'update', 'create', 'delete']).optional(),
|
|
716
|
+
short_code: z
|
|
717
|
+
.string()
|
|
718
|
+
.regex(/^[A-Za-z0-9_-]{1,64}$/)
|
|
719
|
+
.optional(),
|
|
720
|
+
}),
|
|
721
|
+
}),
|
|
722
|
+
toolUseKind: 'edit',
|
|
723
|
+
},
|
|
724
|
+
WaitForMcpServers: {
|
|
725
|
+
...tool({
|
|
726
|
+
description: 'Wait for MCP servers that are still connecting',
|
|
727
|
+
inputSchema: z.object({
|
|
728
|
+
servers: z.array(z.string()).optional(),
|
|
729
|
+
}),
|
|
730
|
+
}),
|
|
731
|
+
toolUseKind: 'readonly',
|
|
732
|
+
},
|
|
733
|
+
Workflow: {
|
|
734
|
+
...tool({
|
|
735
|
+
description: 'Run or resume a dynamic multi-agent workflow',
|
|
736
|
+
inputSchema: z.object({
|
|
737
|
+
script: z.string().optional(),
|
|
738
|
+
name: z.string().optional(),
|
|
739
|
+
description: z.string().optional(),
|
|
740
|
+
title: z.string().optional(),
|
|
741
|
+
args: z.record(z.string(), z.unknown()).optional(),
|
|
742
|
+
scriptPath: z.string().optional(),
|
|
743
|
+
resumeFromRunId: z.string().optional(),
|
|
744
|
+
}),
|
|
745
|
+
}),
|
|
746
|
+
toolUseKind: 'edit',
|
|
747
|
+
},
|
|
394
748
|
} as const satisfies Record<string, HarnessV1BuiltinTool<any, any>>;
|
|
395
749
|
|
|
396
750
|
/*
|
|
@@ -437,6 +791,14 @@ type ClaudeCodeBridgeCoords = z.infer<typeof claudeCodeBridgeCoordsSchema>;
|
|
|
437
791
|
export function createClaudeCode(
|
|
438
792
|
settings: ClaudeCodeHarnessSettings = {},
|
|
439
793
|
): HarnessV1<typeof CLAUDE_CODE_BUILTIN_TOOLS> {
|
|
794
|
+
if (
|
|
795
|
+
settings.mcpServers != null &&
|
|
796
|
+
Object.prototype.hasOwnProperty.call(settings.mcpServers, 'harness-tools')
|
|
797
|
+
) {
|
|
798
|
+
throw new Error(
|
|
799
|
+
'Claude Code MCP server name "harness-tools" is reserved for HarnessAgent tools.',
|
|
800
|
+
);
|
|
801
|
+
}
|
|
440
802
|
let cachedBootstrap: HarnessV1Bootstrap | undefined;
|
|
441
803
|
const thinking = settings.thinking ?? {
|
|
442
804
|
type: 'adaptive',
|
|
@@ -566,6 +928,7 @@ export function createClaudeCode(
|
|
|
566
928
|
permissionMode: startOpts.permissionMode,
|
|
567
929
|
builtinToolFiltering: startOpts.builtinToolFiltering,
|
|
568
930
|
skills: startOpts.skills ?? [],
|
|
931
|
+
mcpServers: settings.mcpServers,
|
|
569
932
|
});
|
|
570
933
|
} catch {
|
|
571
934
|
// Bridge no longer reachable — recover by respawning below.
|
|
@@ -604,7 +967,10 @@ export function createClaudeCode(
|
|
|
604
967
|
})
|
|
605
968
|
: undefined;
|
|
606
969
|
const port = resolveBridgePort(sandboxSession, settings.port);
|
|
607
|
-
const token =
|
|
970
|
+
const token =
|
|
971
|
+
settings.mintBridgeToken == null
|
|
972
|
+
? randomBytes(32).toString('hex')
|
|
973
|
+
: settings.mintBridgeToken(sandboxId);
|
|
608
974
|
const authEnv = resolveClaudeCodeEnv(settings.auth);
|
|
609
975
|
const env = {
|
|
610
976
|
...authEnv,
|
|
@@ -737,6 +1103,7 @@ export function createClaudeCode(
|
|
|
737
1103
|
permissionMode: startOpts.permissionMode,
|
|
738
1104
|
builtinToolFiltering: startOpts.builtinToolFiltering,
|
|
739
1105
|
skills: startOpts.skills ?? [],
|
|
1106
|
+
mcpServers: settings.mcpServers,
|
|
740
1107
|
});
|
|
741
1108
|
},
|
|
742
1109
|
};
|
|
@@ -980,6 +1347,7 @@ function createSession({
|
|
|
980
1347
|
permissionMode,
|
|
981
1348
|
builtinToolFiltering,
|
|
982
1349
|
skills,
|
|
1350
|
+
mcpServers,
|
|
983
1351
|
}: {
|
|
984
1352
|
sessionId: string;
|
|
985
1353
|
channel: ClaudeCodeChannel;
|
|
@@ -999,6 +1367,7 @@ function createSession({
|
|
|
999
1367
|
permissionMode: HarnessV1PermissionMode | undefined;
|
|
1000
1368
|
builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
|
|
1001
1369
|
skills: ReadonlyArray<HarnessV1Skill>;
|
|
1370
|
+
mcpServers: Record<string, unknown> | undefined;
|
|
1002
1371
|
}): HarnessV1Session {
|
|
1003
1372
|
let stopped = false;
|
|
1004
1373
|
let stopPromise: Promise<void> | undefined;
|
|
@@ -1009,13 +1378,6 @@ function createSession({
|
|
|
1009
1378
|
* `attach`ed bridge is already past its first turn and continues on its own.
|
|
1010
1379
|
*/
|
|
1011
1380
|
let pendingResumeFlag = continueOnFirstPrompt;
|
|
1012
|
-
/*
|
|
1013
|
-
* Instructions are prepended to the first user message of a fresh session
|
|
1014
|
-
* only. A resumed session (attach/replay/rerun) already carried them in its
|
|
1015
|
-
* original first message (preserved in the workdir snapshot), so it starts
|
|
1016
|
-
* "applied".
|
|
1017
|
-
*/
|
|
1018
|
-
let instructionsApplied = isResume;
|
|
1019
1381
|
|
|
1020
1382
|
/*
|
|
1021
1383
|
* Wire the channel into one turn's worth of events and return the control
|
|
@@ -1162,20 +1524,17 @@ function createSession({
|
|
|
1162
1524
|
abortSignal: promptOpts.abortSignal,
|
|
1163
1525
|
});
|
|
1164
1526
|
|
|
1165
|
-
let promptText = extractUserText(promptOpts.prompt);
|
|
1166
|
-
if (!instructionsApplied && promptOpts.instructions) {
|
|
1167
|
-
promptText = frameInstructions(promptOpts.instructions, promptText);
|
|
1168
|
-
}
|
|
1169
|
-
instructionsApplied = true;
|
|
1170
|
-
|
|
1171
1527
|
const startMessage = {
|
|
1172
1528
|
type: 'start' as const,
|
|
1173
|
-
prompt:
|
|
1529
|
+
prompt: extractUserText(promptOpts.prompt),
|
|
1174
1530
|
tools: (promptOpts.tools ?? []).map(t => ({
|
|
1175
1531
|
name: t.name,
|
|
1176
1532
|
description: t.description,
|
|
1177
1533
|
inputSchema: t.inputSchema,
|
|
1178
1534
|
})),
|
|
1535
|
+
...(promptOpts.instructions
|
|
1536
|
+
? { instructions: promptOpts.instructions }
|
|
1537
|
+
: {}),
|
|
1179
1538
|
model,
|
|
1180
1539
|
maxTurns,
|
|
1181
1540
|
...(env !== undefined ? { env } : {}),
|
|
@@ -1183,6 +1542,7 @@ function createSession({
|
|
|
1183
1542
|
...(skills.length > 0
|
|
1184
1543
|
? { skills: skills.map(skill => skill.name) }
|
|
1185
1544
|
: {}),
|
|
1545
|
+
...(mcpServers == null ? {} : { mcpServers }),
|
|
1186
1546
|
...(permissionMode ? { permissionMode } : {}),
|
|
1187
1547
|
...(builtinToolFiltering ? { builtinToolFiltering } : {}),
|
|
1188
1548
|
...(debug ? { debug } : {}),
|
|
@@ -1229,6 +1589,9 @@ function createSession({
|
|
|
1229
1589
|
description: t.description,
|
|
1230
1590
|
inputSchema: t.inputSchema,
|
|
1231
1591
|
})),
|
|
1592
|
+
...(continueOpts.instructions
|
|
1593
|
+
? { instructions: continueOpts.instructions }
|
|
1594
|
+
: {}),
|
|
1232
1595
|
model,
|
|
1233
1596
|
maxTurns,
|
|
1234
1597
|
...(env !== undefined ? { env } : {}),
|
|
@@ -1236,6 +1599,7 @@ function createSession({
|
|
|
1236
1599
|
...(skills.length > 0
|
|
1237
1600
|
? { skills: skills.map(skill => skill.name) }
|
|
1238
1601
|
: {}),
|
|
1602
|
+
...(mcpServers == null ? {} : { mcpServers }),
|
|
1239
1603
|
...(permissionMode ? { permissionMode } : {}),
|
|
1240
1604
|
...(builtinToolFiltering ? { builtinToolFiltering } : {}),
|
|
1241
1605
|
...(debug ? { debug } : {}),
|
|
@@ -1423,23 +1787,6 @@ function createSession({
|
|
|
1423
1787
|
};
|
|
1424
1788
|
}
|
|
1425
1789
|
|
|
1426
|
-
/*
|
|
1427
|
-
* Frame session instructions and the user's text so the runtime treats the
|
|
1428
|
-
* instructions as system-provided operating guidance, not something the user
|
|
1429
|
-
* wrote. Without the wrapper the agent can echo the prepended text back as if
|
|
1430
|
-
* the user had asked for it, which is confusing since the user never typed it.
|
|
1431
|
-
* Applied only to the first user message of a fresh session.
|
|
1432
|
-
*/
|
|
1433
|
-
function frameInstructions(instructions: string, userText: string): string {
|
|
1434
|
-
return (
|
|
1435
|
-
'<session-instructions>\n' +
|
|
1436
|
-
'The block below is operating guidance from the system, not a message from the user — follow it, but do not mention it or attribute it to the user.\n\n' +
|
|
1437
|
-
`${instructions}\n` +
|
|
1438
|
-
'</session-instructions>\n\n' +
|
|
1439
|
-
`<user-message>\n${userText}\n</user-message>`
|
|
1440
|
-
);
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
1790
|
/*
|
|
1444
1791
|
* Reduce a `HarnessV1Prompt` to the plain user text the bridge forwards
|
|
1445
1792
|
* to the Claude SDK. File and image parts on the message are not yet
|