@autohq/cli 0.1.259 → 0.1.261
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-bridge.js +164 -60
- package/dist/index.js +174 -63
- package/package.json +1 -1
package/dist/agent-bridge.js
CHANGED
|
@@ -21270,11 +21270,11 @@ var SocketWithoutUpgrade = class _SocketWithoutUpgrade extends Emitter {
|
|
|
21270
21270
|
*/
|
|
21271
21271
|
_resetPingTimeout() {
|
|
21272
21272
|
this.clearTimeoutFn(this._pingTimeoutTimer);
|
|
21273
|
-
const
|
|
21274
|
-
this._pingTimeoutTime = Date.now() +
|
|
21273
|
+
const delay2 = this._pingInterval + this._pingTimeout;
|
|
21274
|
+
this._pingTimeoutTime = Date.now() + delay2;
|
|
21275
21275
|
this._pingTimeoutTimer = this.setTimeoutFn(() => {
|
|
21276
21276
|
this._onClose("ping timeout");
|
|
21277
|
-
},
|
|
21277
|
+
}, delay2);
|
|
21278
21278
|
if (this.opts.autoUnref) {
|
|
21279
21279
|
this._pingTimeoutTimer.unref();
|
|
21280
21280
|
}
|
|
@@ -23255,8 +23255,8 @@ var Manager = class extends Emitter {
|
|
|
23255
23255
|
this.emitReserved("reconnect_failed");
|
|
23256
23256
|
this._reconnecting = false;
|
|
23257
23257
|
} else {
|
|
23258
|
-
const
|
|
23259
|
-
debug10("will wait %dms before reconnect attempt",
|
|
23258
|
+
const delay2 = this.backoff.duration();
|
|
23259
|
+
debug10("will wait %dms before reconnect attempt", delay2);
|
|
23260
23260
|
this._reconnecting = true;
|
|
23261
23261
|
const timer = this.setTimeoutFn(() => {
|
|
23262
23262
|
if (self2.skipReconnect)
|
|
@@ -23276,7 +23276,7 @@ var Manager = class extends Emitter {
|
|
|
23276
23276
|
self2.onreconnect();
|
|
23277
23277
|
}
|
|
23278
23278
|
});
|
|
23279
|
-
},
|
|
23279
|
+
}, delay2);
|
|
23280
23280
|
if (this.opts.autoUnref) {
|
|
23281
23281
|
timer.unref();
|
|
23282
23282
|
}
|
|
@@ -23340,7 +23340,7 @@ Object.assign(lookup, {
|
|
|
23340
23340
|
// package.json
|
|
23341
23341
|
var package_default = {
|
|
23342
23342
|
name: "@autohq/cli",
|
|
23343
|
-
version: "0.1.
|
|
23343
|
+
version: "0.1.261",
|
|
23344
23344
|
license: "SEE LICENSE IN README.md",
|
|
23345
23345
|
publishConfig: {
|
|
23346
23346
|
access: "public"
|
|
@@ -27877,60 +27877,50 @@ function compareSemver(left, right) {
|
|
|
27877
27877
|
return 0;
|
|
27878
27878
|
}
|
|
27879
27879
|
|
|
27880
|
+
// ../../packages/schemas/src/templates/content.generated.ts
|
|
27881
|
+
var GENERATED_TEMPLATE_CONTENT = {
|
|
27882
|
+
"@auto/onboarding": [
|
|
27883
|
+
{
|
|
27884
|
+
version: "1.0.0",
|
|
27885
|
+
files: [
|
|
27886
|
+
{
|
|
27887
|
+
path: "fragments/onboarding.yaml",
|
|
27888
|
+
content: "# Managed onboarding fragment (@auto/onboarding). Tenant onboarding agents\n# import this to inherit Auto's house onboarding guidance. Tenant fields win\n# on merge, so house tweaks can be layered on top of this base.\nsystemPrompt: |\n You are an Auto onboarding guide. Greet the user warmly, explain that Auto\n lets them compose agents and triggers into automated workflows from simple\n `.auto/` YAML, and guide them to their first deployed workflow. Keep replies\n short and conversational, ask one question at a time, and verify each step\n actually worked before telling the user it did."
|
|
27889
|
+
}
|
|
27890
|
+
]
|
|
27891
|
+
}
|
|
27892
|
+
],
|
|
27893
|
+
"@auto/smoke-test": [
|
|
27894
|
+
{
|
|
27895
|
+
version: "1.0.0",
|
|
27896
|
+
files: [
|
|
27897
|
+
{
|
|
27898
|
+
path: "fragments/smoke-test.yaml",
|
|
27899
|
+
content: "# Managed smoke-test fragment (@auto/smoke-test). A minimal, self-contained\n# fixture for verifying that managed templates resolve and inject cleanly.\n# Importing agents inherit this base guidance; tenant fields win on merge.\nsystemPrompt: |\n You are the Auto smoke-test agent, a minimal fixture used to confirm that\n managed templates resolve and inject correctly. When asked to run the smoke\n test, reply with a single short sentence confirming it passed, and do\n nothing else."
|
|
27900
|
+
}
|
|
27901
|
+
]
|
|
27902
|
+
}
|
|
27903
|
+
]
|
|
27904
|
+
};
|
|
27905
|
+
|
|
27880
27906
|
// ../../packages/schemas/src/templates/hardcoded.ts
|
|
27881
|
-
var
|
|
27882
|
-
"
|
|
27883
|
-
"
|
|
27884
|
-
|
|
27885
|
-
|
|
27886
|
-
|
|
27887
|
-
|
|
27888
|
-
|
|
27889
|
-
|
|
27890
|
-
|
|
27891
|
-
|
|
27892
|
-
|
|
27893
|
-
|
|
27894
|
-
|
|
27895
|
-
|
|
27896
|
-
|
|
27897
|
-
|
|
27898
|
-
" managed templates resolve and inject correctly. When asked to run the smoke",
|
|
27899
|
-
" test, reply with a single short sentence confirming it passed, and do",
|
|
27900
|
-
" nothing else."
|
|
27901
|
-
].join("\n");
|
|
27902
|
-
var MANAGED_TEMPLATES = [
|
|
27903
|
-
{
|
|
27904
|
-
name: "@auto/onboarding",
|
|
27905
|
-
description: "Auto's house onboarding guidance, importable as a managed template.",
|
|
27906
|
-
versions: [
|
|
27907
|
-
{
|
|
27908
|
-
version: "1.0.0",
|
|
27909
|
-
files: [
|
|
27910
|
-
{
|
|
27911
|
-
path: "fragments/onboarding.yaml",
|
|
27912
|
-
content: ONBOARDING_FRAGMENT_V1
|
|
27913
|
-
}
|
|
27914
|
-
]
|
|
27915
|
-
}
|
|
27916
|
-
]
|
|
27917
|
-
},
|
|
27918
|
-
{
|
|
27919
|
-
name: "@auto/smoke-test",
|
|
27920
|
-
description: "A minimal managed-template smoke-test fixture for end-to-end verification.",
|
|
27921
|
-
versions: [
|
|
27922
|
-
{
|
|
27923
|
-
version: "1.0.0",
|
|
27924
|
-
files: [
|
|
27925
|
-
{
|
|
27926
|
-
path: "fragments/smoke-test.yaml",
|
|
27927
|
-
content: SMOKE_TEST_FRAGMENT_V1
|
|
27928
|
-
}
|
|
27929
|
-
]
|
|
27930
|
-
}
|
|
27931
|
-
]
|
|
27932
|
-
}
|
|
27933
|
-
];
|
|
27907
|
+
var TEMPLATE_DESCRIPTIONS = {
|
|
27908
|
+
"@auto/onboarding": "Auto's house onboarding guidance, importable as a managed template.",
|
|
27909
|
+
"@auto/smoke-test": "A minimal managed-template smoke-test fixture for end-to-end verification."
|
|
27910
|
+
};
|
|
27911
|
+
var MANAGED_TEMPLATES = Object.entries(
|
|
27912
|
+
GENERATED_TEMPLATE_CONTENT
|
|
27913
|
+
).map(([name, versions]) => ({
|
|
27914
|
+
name,
|
|
27915
|
+
description: requireDescription(name),
|
|
27916
|
+
versions: versions.map((version2) => ({
|
|
27917
|
+
version: version2.version,
|
|
27918
|
+
files: version2.files.map((file2) => ({
|
|
27919
|
+
path: file2.path,
|
|
27920
|
+
content: file2.content
|
|
27921
|
+
}))
|
|
27922
|
+
}))
|
|
27923
|
+
}));
|
|
27934
27924
|
var HardcodedTemplateRegistry = class {
|
|
27935
27925
|
templates;
|
|
27936
27926
|
constructor(templates = MANAGED_TEMPLATES) {
|
|
@@ -27980,6 +27970,13 @@ var HardcodedTemplateRegistry = class {
|
|
|
27980
27970
|
}
|
|
27981
27971
|
};
|
|
27982
27972
|
var defaultTemplateRegistry = new HardcodedTemplateRegistry();
|
|
27973
|
+
function requireDescription(name) {
|
|
27974
|
+
const description = TEMPLATE_DESCRIPTIONS[name];
|
|
27975
|
+
if (!description) {
|
|
27976
|
+
throw new Error(`Managed template ${name} is missing a description`);
|
|
27977
|
+
}
|
|
27978
|
+
return description;
|
|
27979
|
+
}
|
|
27983
27980
|
function toBundleFile(file2) {
|
|
27984
27981
|
return {
|
|
27985
27982
|
path: file2.path,
|
|
@@ -48192,6 +48189,8 @@ function withClaudeStderrDiagnosticsPointer(error51, capturedStderr) {
|
|
|
48192
48189
|
// src/commands/agent-bridge/harness/claude-code/session.ts
|
|
48193
48190
|
var CLAUDE_AGENT_STARTUP_TIMEOUT_MS = 3e4;
|
|
48194
48191
|
var CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS = 1e4;
|
|
48192
|
+
var CLAUDE_MCP_REGISTRATION_TIMEOUT_MS = 3e3;
|
|
48193
|
+
var CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS = 100;
|
|
48195
48194
|
var CLAUDE_STARTUP_PROFILE_HOOK_EVENTS = [
|
|
48196
48195
|
"Setup",
|
|
48197
48196
|
"SessionStart",
|
|
@@ -48239,6 +48238,12 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
48239
48238
|
// message lands only once the interrupted turn has settled its tool_use.
|
|
48240
48239
|
turnResultCount = 0;
|
|
48241
48240
|
turnSettlementWaiters = /* @__PURE__ */ new Set();
|
|
48241
|
+
// Memoized MCP-registration gate, keyed by the attached query. A runtime
|
|
48242
|
+
// restart warms a brand-new session + query, so keying off the query object
|
|
48243
|
+
// (rather than a session-lifetime flag) re-arms the gate on every restart:
|
|
48244
|
+
// each fresh warm gates its own first turn, while later turns on the same
|
|
48245
|
+
// query share the resolved promise and never re-poll.
|
|
48246
|
+
mcpRegistrationGate = null;
|
|
48242
48247
|
constructor(input) {
|
|
48243
48248
|
const optionsStartedAt = Date.now();
|
|
48244
48249
|
this.input = input;
|
|
@@ -48301,6 +48306,7 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
48301
48306
|
return;
|
|
48302
48307
|
}
|
|
48303
48308
|
await this.interruptActiveTurnBeforeMessage();
|
|
48309
|
+
await this.awaitMcpRegistration();
|
|
48304
48310
|
this.enqueueUserMessage(message);
|
|
48305
48311
|
this.input.runtimeLogger?.info("agent_bridge_claude_send_message_queued", {
|
|
48306
48312
|
delivery_mode: mode,
|
|
@@ -48588,6 +48594,86 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
48588
48594
|
}
|
|
48589
48595
|
}
|
|
48590
48596
|
}
|
|
48597
|
+
// Why this gate is correct without a provider-backed test (it relies on real
|
|
48598
|
+
// SDK mcpServerStatus() timing that fakes cannot prove):
|
|
48599
|
+
// - Safe-degrade: pollMcpRegistration never rejects — on timeout, a
|
|
48600
|
+
// status-read failure, or a closed session it resolves, so the worst case
|
|
48601
|
+
// is today's immediate injection. The gate can never regress below the
|
|
48602
|
+
// status-quo race; it can only close it.
|
|
48603
|
+
// - Provider-path validation is deferred deliberately: the failure is the
|
|
48604
|
+
// intermittent live-prod race on the chief-of-staff singleton (a real
|
|
48605
|
+
// runtime restart dispatching turn-1 while real mcp__* servers reconnect),
|
|
48606
|
+
// which cannot be reproduced deterministically without live credentials
|
|
48607
|
+
// and a forced restart racing reconnect.
|
|
48608
|
+
// - Live confirmation comes from the agent_bridge_claude_mcp_gate_started/
|
|
48609
|
+
// _ready/_timeout diagnostics on the next real restart, not a CI fake.
|
|
48610
|
+
//
|
|
48611
|
+
// Block until the freshly-warmed query's MCP servers have left `pending`, so
|
|
48612
|
+
// the first turn's prompt is built with the mcp__* tools registered. Memoized
|
|
48613
|
+
// per query: the first send pays the wait, concurrent first sends share it,
|
|
48614
|
+
// and a later turn on the same query returns immediately. Sessions without
|
|
48615
|
+
// configured MCP servers, or whose query failed to attach, skip the gate and
|
|
48616
|
+
// fall through to the existing enqueue/error path unchanged.
|
|
48617
|
+
async awaitMcpRegistration() {
|
|
48618
|
+
if (Object.keys(this.options.mcpServers ?? {}).length === 0) {
|
|
48619
|
+
return;
|
|
48620
|
+
}
|
|
48621
|
+
let query;
|
|
48622
|
+
try {
|
|
48623
|
+
query = await this.ensureRunningQuery();
|
|
48624
|
+
} catch {
|
|
48625
|
+
return;
|
|
48626
|
+
}
|
|
48627
|
+
if (this.mcpRegistrationGate?.query !== query) {
|
|
48628
|
+
this.mcpRegistrationGate = {
|
|
48629
|
+
query,
|
|
48630
|
+
promise: this.pollMcpRegistration(query)
|
|
48631
|
+
};
|
|
48632
|
+
}
|
|
48633
|
+
await this.mcpRegistrationGate.promise;
|
|
48634
|
+
}
|
|
48635
|
+
// Poll the live query's MCP server status until no configured server is still
|
|
48636
|
+
// `pending`, bounded by CLAUDE_MCP_REGISTRATION_TIMEOUT_MS. Terminal states
|
|
48637
|
+
// (connected/failed/needs-auth/disabled) all count as settled, so a server
|
|
48638
|
+
// that cannot connect in this runtime (e.g. an interactively-authed server in
|
|
48639
|
+
// a headless sandbox) never holds the gate to the timeout. Never rejects:
|
|
48640
|
+
// status-read failure, timeout, or a closed session all resolve so the caller
|
|
48641
|
+
// degrades to immediate injection.
|
|
48642
|
+
async pollMcpRegistration(query) {
|
|
48643
|
+
const configuredNames = Object.keys(this.options.mcpServers ?? {});
|
|
48644
|
+
const startedAt = Date.now();
|
|
48645
|
+
const deadline = startedAt + CLAUDE_MCP_REGISTRATION_TIMEOUT_MS;
|
|
48646
|
+
this.input.writeOutput?.(
|
|
48647
|
+
`agent_bridge_claude_mcp_gate_started servers=${configuredNames.length}`
|
|
48648
|
+
);
|
|
48649
|
+
while (true) {
|
|
48650
|
+
if (this.state.kind === "closed") {
|
|
48651
|
+
return;
|
|
48652
|
+
}
|
|
48653
|
+
let statuses;
|
|
48654
|
+
try {
|
|
48655
|
+
statuses = await query.mcpServerStatus();
|
|
48656
|
+
} catch (error51) {
|
|
48657
|
+
this.input.writeOutput?.(
|
|
48658
|
+
`agent_bridge_claude_mcp_gate_status_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
|
|
48659
|
+
);
|
|
48660
|
+
return;
|
|
48661
|
+
}
|
|
48662
|
+
if (mcpServersSettled(statuses, configuredNames)) {
|
|
48663
|
+
this.input.writeOutput?.(
|
|
48664
|
+
`agent_bridge_claude_mcp_gate_ready duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
|
|
48665
|
+
);
|
|
48666
|
+
return;
|
|
48667
|
+
}
|
|
48668
|
+
if (Date.now() >= deadline) {
|
|
48669
|
+
this.input.writeOutput?.(
|
|
48670
|
+
`agent_bridge_claude_mcp_gate_timeout duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
|
|
48671
|
+
);
|
|
48672
|
+
return;
|
|
48673
|
+
}
|
|
48674
|
+
await delay(CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS);
|
|
48675
|
+
}
|
|
48676
|
+
}
|
|
48591
48677
|
messageProbeContext() {
|
|
48592
48678
|
return {
|
|
48593
48679
|
configuredMcpServerNames: Object.keys(this.options.mcpServers ?? {}),
|
|
@@ -48717,6 +48803,24 @@ var AsyncMessageQueue = class {
|
|
|
48717
48803
|
});
|
|
48718
48804
|
}
|
|
48719
48805
|
};
|
|
48806
|
+
function mcpServersSettled(statuses, configuredNames) {
|
|
48807
|
+
const statusByName = new Map(
|
|
48808
|
+
statuses.map((server) => [server.name, server.status])
|
|
48809
|
+
);
|
|
48810
|
+
return configuredNames.every((name) => {
|
|
48811
|
+
const status = statusByName.get(name);
|
|
48812
|
+
return status !== void 0 && status !== "pending";
|
|
48813
|
+
});
|
|
48814
|
+
}
|
|
48815
|
+
function mcpStatusList(statuses) {
|
|
48816
|
+
return statuses.map((server) => `${server.name}:${server.status}`).join(",");
|
|
48817
|
+
}
|
|
48818
|
+
function delay(ms) {
|
|
48819
|
+
return new Promise((resolve) => {
|
|
48820
|
+
const timer = setTimeout(resolve, ms);
|
|
48821
|
+
timer.unref?.();
|
|
48822
|
+
});
|
|
48823
|
+
}
|
|
48720
48824
|
function claudeAgentUserMessage(message) {
|
|
48721
48825
|
return {
|
|
48722
48826
|
type: "user",
|
package/dist/index.js
CHANGED
|
@@ -19771,7 +19771,46 @@ var init_registry = __esm({
|
|
|
19771
19771
|
}
|
|
19772
19772
|
});
|
|
19773
19773
|
|
|
19774
|
+
// ../../packages/schemas/src/templates/content.generated.ts
|
|
19775
|
+
var GENERATED_TEMPLATE_CONTENT;
|
|
19776
|
+
var init_content_generated = __esm({
|
|
19777
|
+
"../../packages/schemas/src/templates/content.generated.ts"() {
|
|
19778
|
+
"use strict";
|
|
19779
|
+
GENERATED_TEMPLATE_CONTENT = {
|
|
19780
|
+
"@auto/onboarding": [
|
|
19781
|
+
{
|
|
19782
|
+
version: "1.0.0",
|
|
19783
|
+
files: [
|
|
19784
|
+
{
|
|
19785
|
+
path: "fragments/onboarding.yaml",
|
|
19786
|
+
content: "# Managed onboarding fragment (@auto/onboarding). Tenant onboarding agents\n# import this to inherit Auto's house onboarding guidance. Tenant fields win\n# on merge, so house tweaks can be layered on top of this base.\nsystemPrompt: |\n You are an Auto onboarding guide. Greet the user warmly, explain that Auto\n lets them compose agents and triggers into automated workflows from simple\n `.auto/` YAML, and guide them to their first deployed workflow. Keep replies\n short and conversational, ask one question at a time, and verify each step\n actually worked before telling the user it did."
|
|
19787
|
+
}
|
|
19788
|
+
]
|
|
19789
|
+
}
|
|
19790
|
+
],
|
|
19791
|
+
"@auto/smoke-test": [
|
|
19792
|
+
{
|
|
19793
|
+
version: "1.0.0",
|
|
19794
|
+
files: [
|
|
19795
|
+
{
|
|
19796
|
+
path: "fragments/smoke-test.yaml",
|
|
19797
|
+
content: "# Managed smoke-test fragment (@auto/smoke-test). A minimal, self-contained\n# fixture for verifying that managed templates resolve and inject cleanly.\n# Importing agents inherit this base guidance; tenant fields win on merge.\nsystemPrompt: |\n You are the Auto smoke-test agent, a minimal fixture used to confirm that\n managed templates resolve and inject correctly. When asked to run the smoke\n test, reply with a single short sentence confirming it passed, and do\n nothing else."
|
|
19798
|
+
}
|
|
19799
|
+
]
|
|
19800
|
+
}
|
|
19801
|
+
]
|
|
19802
|
+
};
|
|
19803
|
+
}
|
|
19804
|
+
});
|
|
19805
|
+
|
|
19774
19806
|
// ../../packages/schemas/src/templates/hardcoded.ts
|
|
19807
|
+
function requireDescription(name) {
|
|
19808
|
+
const description = TEMPLATE_DESCRIPTIONS[name];
|
|
19809
|
+
if (!description) {
|
|
19810
|
+
throw new Error(`Managed template ${name} is missing a description`);
|
|
19811
|
+
}
|
|
19812
|
+
return description;
|
|
19813
|
+
}
|
|
19775
19814
|
function toBundleFile(file2) {
|
|
19776
19815
|
return {
|
|
19777
19816
|
path: file2.path,
|
|
@@ -19788,64 +19827,29 @@ function toManagedTemplate(template) {
|
|
|
19788
19827
|
versions: template.versions.map(toTemplateVersion)
|
|
19789
19828
|
};
|
|
19790
19829
|
}
|
|
19791
|
-
var
|
|
19830
|
+
var TEMPLATE_DESCRIPTIONS, MANAGED_TEMPLATES, HardcodedTemplateRegistry, defaultTemplateRegistry;
|
|
19792
19831
|
var init_hardcoded = __esm({
|
|
19793
19832
|
"../../packages/schemas/src/templates/hardcoded.ts"() {
|
|
19794
19833
|
"use strict";
|
|
19834
|
+
init_content_generated();
|
|
19795
19835
|
init_registry();
|
|
19796
|
-
|
|
19797
|
-
"
|
|
19798
|
-
"
|
|
19799
|
-
|
|
19800
|
-
|
|
19801
|
-
|
|
19802
|
-
|
|
19803
|
-
|
|
19804
|
-
|
|
19805
|
-
|
|
19806
|
-
|
|
19807
|
-
|
|
19808
|
-
|
|
19809
|
-
|
|
19810
|
-
|
|
19811
|
-
|
|
19812
|
-
|
|
19813
|
-
" managed templates resolve and inject correctly. When asked to run the smoke",
|
|
19814
|
-
" test, reply with a single short sentence confirming it passed, and do",
|
|
19815
|
-
" nothing else."
|
|
19816
|
-
].join("\n");
|
|
19817
|
-
MANAGED_TEMPLATES = [
|
|
19818
|
-
{
|
|
19819
|
-
name: "@auto/onboarding",
|
|
19820
|
-
description: "Auto's house onboarding guidance, importable as a managed template.",
|
|
19821
|
-
versions: [
|
|
19822
|
-
{
|
|
19823
|
-
version: "1.0.0",
|
|
19824
|
-
files: [
|
|
19825
|
-
{
|
|
19826
|
-
path: "fragments/onboarding.yaml",
|
|
19827
|
-
content: ONBOARDING_FRAGMENT_V1
|
|
19828
|
-
}
|
|
19829
|
-
]
|
|
19830
|
-
}
|
|
19831
|
-
]
|
|
19832
|
-
},
|
|
19833
|
-
{
|
|
19834
|
-
name: "@auto/smoke-test",
|
|
19835
|
-
description: "A minimal managed-template smoke-test fixture for end-to-end verification.",
|
|
19836
|
-
versions: [
|
|
19837
|
-
{
|
|
19838
|
-
version: "1.0.0",
|
|
19839
|
-
files: [
|
|
19840
|
-
{
|
|
19841
|
-
path: "fragments/smoke-test.yaml",
|
|
19842
|
-
content: SMOKE_TEST_FRAGMENT_V1
|
|
19843
|
-
}
|
|
19844
|
-
]
|
|
19845
|
-
}
|
|
19846
|
-
]
|
|
19847
|
-
}
|
|
19848
|
-
];
|
|
19836
|
+
TEMPLATE_DESCRIPTIONS = {
|
|
19837
|
+
"@auto/onboarding": "Auto's house onboarding guidance, importable as a managed template.",
|
|
19838
|
+
"@auto/smoke-test": "A minimal managed-template smoke-test fixture for end-to-end verification."
|
|
19839
|
+
};
|
|
19840
|
+
MANAGED_TEMPLATES = Object.entries(
|
|
19841
|
+
GENERATED_TEMPLATE_CONTENT
|
|
19842
|
+
).map(([name, versions]) => ({
|
|
19843
|
+
name,
|
|
19844
|
+
description: requireDescription(name),
|
|
19845
|
+
versions: versions.map((version2) => ({
|
|
19846
|
+
version: version2.version,
|
|
19847
|
+
files: version2.files.map((file2) => ({
|
|
19848
|
+
path: file2.path,
|
|
19849
|
+
content: file2.content
|
|
19850
|
+
}))
|
|
19851
|
+
}))
|
|
19852
|
+
}));
|
|
19849
19853
|
HardcodedTemplateRegistry = class {
|
|
19850
19854
|
templates;
|
|
19851
19855
|
constructor(templates = MANAGED_TEMPLATES) {
|
|
@@ -22782,7 +22786,7 @@ var init_package = __esm({
|
|
|
22782
22786
|
"package.json"() {
|
|
22783
22787
|
package_default = {
|
|
22784
22788
|
name: "@autohq/cli",
|
|
22785
|
-
version: "0.1.
|
|
22789
|
+
version: "0.1.261",
|
|
22786
22790
|
license: "SEE LICENSE IN README.md",
|
|
22787
22791
|
publishConfig: {
|
|
22788
22792
|
access: "public"
|
|
@@ -33655,6 +33659,8 @@ function withClaudeStderrDiagnosticsPointer(error51, capturedStderr) {
|
|
|
33655
33659
|
// src/commands/agent-bridge/harness/claude-code/session.ts
|
|
33656
33660
|
var CLAUDE_AGENT_STARTUP_TIMEOUT_MS = 3e4;
|
|
33657
33661
|
var CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS = 1e4;
|
|
33662
|
+
var CLAUDE_MCP_REGISTRATION_TIMEOUT_MS = 3e3;
|
|
33663
|
+
var CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS = 100;
|
|
33658
33664
|
var CLAUDE_STARTUP_PROFILE_HOOK_EVENTS = [
|
|
33659
33665
|
"Setup",
|
|
33660
33666
|
"SessionStart",
|
|
@@ -33702,6 +33708,12 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
33702
33708
|
// message lands only once the interrupted turn has settled its tool_use.
|
|
33703
33709
|
turnResultCount = 0;
|
|
33704
33710
|
turnSettlementWaiters = /* @__PURE__ */ new Set();
|
|
33711
|
+
// Memoized MCP-registration gate, keyed by the attached query. A runtime
|
|
33712
|
+
// restart warms a brand-new session + query, so keying off the query object
|
|
33713
|
+
// (rather than a session-lifetime flag) re-arms the gate on every restart:
|
|
33714
|
+
// each fresh warm gates its own first turn, while later turns on the same
|
|
33715
|
+
// query share the resolved promise and never re-poll.
|
|
33716
|
+
mcpRegistrationGate = null;
|
|
33705
33717
|
constructor(input) {
|
|
33706
33718
|
const optionsStartedAt = Date.now();
|
|
33707
33719
|
this.input = input;
|
|
@@ -33764,6 +33776,7 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
33764
33776
|
return;
|
|
33765
33777
|
}
|
|
33766
33778
|
await this.interruptActiveTurnBeforeMessage();
|
|
33779
|
+
await this.awaitMcpRegistration();
|
|
33767
33780
|
this.enqueueUserMessage(message);
|
|
33768
33781
|
this.input.runtimeLogger?.info("agent_bridge_claude_send_message_queued", {
|
|
33769
33782
|
delivery_mode: mode,
|
|
@@ -34051,6 +34064,86 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
34051
34064
|
}
|
|
34052
34065
|
}
|
|
34053
34066
|
}
|
|
34067
|
+
// Why this gate is correct without a provider-backed test (it relies on real
|
|
34068
|
+
// SDK mcpServerStatus() timing that fakes cannot prove):
|
|
34069
|
+
// - Safe-degrade: pollMcpRegistration never rejects — on timeout, a
|
|
34070
|
+
// status-read failure, or a closed session it resolves, so the worst case
|
|
34071
|
+
// is today's immediate injection. The gate can never regress below the
|
|
34072
|
+
// status-quo race; it can only close it.
|
|
34073
|
+
// - Provider-path validation is deferred deliberately: the failure is the
|
|
34074
|
+
// intermittent live-prod race on the chief-of-staff singleton (a real
|
|
34075
|
+
// runtime restart dispatching turn-1 while real mcp__* servers reconnect),
|
|
34076
|
+
// which cannot be reproduced deterministically without live credentials
|
|
34077
|
+
// and a forced restart racing reconnect.
|
|
34078
|
+
// - Live confirmation comes from the agent_bridge_claude_mcp_gate_started/
|
|
34079
|
+
// _ready/_timeout diagnostics on the next real restart, not a CI fake.
|
|
34080
|
+
//
|
|
34081
|
+
// Block until the freshly-warmed query's MCP servers have left `pending`, so
|
|
34082
|
+
// the first turn's prompt is built with the mcp__* tools registered. Memoized
|
|
34083
|
+
// per query: the first send pays the wait, concurrent first sends share it,
|
|
34084
|
+
// and a later turn on the same query returns immediately. Sessions without
|
|
34085
|
+
// configured MCP servers, or whose query failed to attach, skip the gate and
|
|
34086
|
+
// fall through to the existing enqueue/error path unchanged.
|
|
34087
|
+
async awaitMcpRegistration() {
|
|
34088
|
+
if (Object.keys(this.options.mcpServers ?? {}).length === 0) {
|
|
34089
|
+
return;
|
|
34090
|
+
}
|
|
34091
|
+
let query;
|
|
34092
|
+
try {
|
|
34093
|
+
query = await this.ensureRunningQuery();
|
|
34094
|
+
} catch {
|
|
34095
|
+
return;
|
|
34096
|
+
}
|
|
34097
|
+
if (this.mcpRegistrationGate?.query !== query) {
|
|
34098
|
+
this.mcpRegistrationGate = {
|
|
34099
|
+
query,
|
|
34100
|
+
promise: this.pollMcpRegistration(query)
|
|
34101
|
+
};
|
|
34102
|
+
}
|
|
34103
|
+
await this.mcpRegistrationGate.promise;
|
|
34104
|
+
}
|
|
34105
|
+
// Poll the live query's MCP server status until no configured server is still
|
|
34106
|
+
// `pending`, bounded by CLAUDE_MCP_REGISTRATION_TIMEOUT_MS. Terminal states
|
|
34107
|
+
// (connected/failed/needs-auth/disabled) all count as settled, so a server
|
|
34108
|
+
// that cannot connect in this runtime (e.g. an interactively-authed server in
|
|
34109
|
+
// a headless sandbox) never holds the gate to the timeout. Never rejects:
|
|
34110
|
+
// status-read failure, timeout, or a closed session all resolve so the caller
|
|
34111
|
+
// degrades to immediate injection.
|
|
34112
|
+
async pollMcpRegistration(query) {
|
|
34113
|
+
const configuredNames = Object.keys(this.options.mcpServers ?? {});
|
|
34114
|
+
const startedAt = Date.now();
|
|
34115
|
+
const deadline = startedAt + CLAUDE_MCP_REGISTRATION_TIMEOUT_MS;
|
|
34116
|
+
this.input.writeOutput?.(
|
|
34117
|
+
`agent_bridge_claude_mcp_gate_started servers=${configuredNames.length}`
|
|
34118
|
+
);
|
|
34119
|
+
while (true) {
|
|
34120
|
+
if (this.state.kind === "closed") {
|
|
34121
|
+
return;
|
|
34122
|
+
}
|
|
34123
|
+
let statuses;
|
|
34124
|
+
try {
|
|
34125
|
+
statuses = await query.mcpServerStatus();
|
|
34126
|
+
} catch (error51) {
|
|
34127
|
+
this.input.writeOutput?.(
|
|
34128
|
+
`agent_bridge_claude_mcp_gate_status_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
|
|
34129
|
+
);
|
|
34130
|
+
return;
|
|
34131
|
+
}
|
|
34132
|
+
if (mcpServersSettled(statuses, configuredNames)) {
|
|
34133
|
+
this.input.writeOutput?.(
|
|
34134
|
+
`agent_bridge_claude_mcp_gate_ready duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
|
|
34135
|
+
);
|
|
34136
|
+
return;
|
|
34137
|
+
}
|
|
34138
|
+
if (Date.now() >= deadline) {
|
|
34139
|
+
this.input.writeOutput?.(
|
|
34140
|
+
`agent_bridge_claude_mcp_gate_timeout duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
|
|
34141
|
+
);
|
|
34142
|
+
return;
|
|
34143
|
+
}
|
|
34144
|
+
await delay(CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS);
|
|
34145
|
+
}
|
|
34146
|
+
}
|
|
34054
34147
|
messageProbeContext() {
|
|
34055
34148
|
return {
|
|
34056
34149
|
configuredMcpServerNames: Object.keys(this.options.mcpServers ?? {}),
|
|
@@ -34180,6 +34273,24 @@ var AsyncMessageQueue = class {
|
|
|
34180
34273
|
});
|
|
34181
34274
|
}
|
|
34182
34275
|
};
|
|
34276
|
+
function mcpServersSettled(statuses, configuredNames) {
|
|
34277
|
+
const statusByName = new Map(
|
|
34278
|
+
statuses.map((server) => [server.name, server.status])
|
|
34279
|
+
);
|
|
34280
|
+
return configuredNames.every((name) => {
|
|
34281
|
+
const status = statusByName.get(name);
|
|
34282
|
+
return status !== void 0 && status !== "pending";
|
|
34283
|
+
});
|
|
34284
|
+
}
|
|
34285
|
+
function mcpStatusList(statuses) {
|
|
34286
|
+
return statuses.map((server) => `${server.name}:${server.status}`).join(",");
|
|
34287
|
+
}
|
|
34288
|
+
function delay(ms) {
|
|
34289
|
+
return new Promise((resolve4) => {
|
|
34290
|
+
const timer = setTimeout(resolve4, ms);
|
|
34291
|
+
timer.unref?.();
|
|
34292
|
+
});
|
|
34293
|
+
}
|
|
34183
34294
|
function claudeAgentUserMessage(message) {
|
|
34184
34295
|
return {
|
|
34185
34296
|
type: "user",
|
|
@@ -37819,7 +37930,7 @@ function selectLastLines(content, count) {
|
|
|
37819
37930
|
async function followRuntimeLog(input) {
|
|
37820
37931
|
let offset = input.fromByte;
|
|
37821
37932
|
while (!input.signal?.aborted) {
|
|
37822
|
-
await
|
|
37933
|
+
await delay2(input.pollIntervalMs, input.signal);
|
|
37823
37934
|
if (input.signal?.aborted) {
|
|
37824
37935
|
return;
|
|
37825
37936
|
}
|
|
@@ -37854,7 +37965,7 @@ function readByteRange(path2, offset, length) {
|
|
|
37854
37965
|
}
|
|
37855
37966
|
return buffer.toString("utf8");
|
|
37856
37967
|
}
|
|
37857
|
-
function
|
|
37968
|
+
function delay2(ms, signal) {
|
|
37858
37969
|
return new Promise((resolve4) => {
|
|
37859
37970
|
const timer = setTimeout(resolve4, ms);
|
|
37860
37971
|
signal?.addEventListener(
|
|
@@ -38800,10 +38911,10 @@ async function runConsole(input) {
|
|
|
38800
38911
|
input.writeOutput(
|
|
38801
38912
|
`stream interrupted: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
38802
38913
|
);
|
|
38803
|
-
await
|
|
38914
|
+
await delay3(1e3, abort.signal);
|
|
38804
38915
|
continue;
|
|
38805
38916
|
}
|
|
38806
|
-
await
|
|
38917
|
+
await delay3(250, abort.signal);
|
|
38807
38918
|
}
|
|
38808
38919
|
})();
|
|
38809
38920
|
const consoleInput = createInteractiveInputController({
|
|
@@ -38883,7 +38994,7 @@ function createInteractiveInputController(input) {
|
|
|
38883
38994
|
})();
|
|
38884
38995
|
return { done, close };
|
|
38885
38996
|
}
|
|
38886
|
-
function
|
|
38997
|
+
function delay3(ms, signal) {
|
|
38887
38998
|
if (signal.aborted) {
|
|
38888
38999
|
return Promise.resolve();
|
|
38889
39000
|
}
|
|
@@ -39204,7 +39315,7 @@ async function pollUntilAwaiting(input) {
|
|
|
39204
39315
|
if (firstPoll) {
|
|
39205
39316
|
firstPoll = false;
|
|
39206
39317
|
} else {
|
|
39207
|
-
await
|
|
39318
|
+
await delay4(
|
|
39208
39319
|
Math.min(input.pollIntervalMs, Math.max(deadline - Date.now(), 0))
|
|
39209
39320
|
);
|
|
39210
39321
|
}
|
|
@@ -39218,7 +39329,7 @@ async function pollUntilAwaiting(input) {
|
|
|
39218
39329
|
status = session.status;
|
|
39219
39330
|
}
|
|
39220
39331
|
}
|
|
39221
|
-
function
|
|
39332
|
+
function delay4(ms) {
|
|
39222
39333
|
if (ms <= 0) {
|
|
39223
39334
|
return Promise.resolve();
|
|
39224
39335
|
}
|
|
@@ -39278,7 +39389,7 @@ async function streamDiagnosticsBestEffort(input) {
|
|
|
39278
39389
|
}
|
|
39279
39390
|
}
|
|
39280
39391
|
async function settleDiagnostics(done) {
|
|
39281
|
-
await Promise.race([done,
|
|
39392
|
+
await Promise.race([done, delay4(DIAGNOSTICS_SHUTDOWN_WAIT_MS)]);
|
|
39282
39393
|
}
|
|
39283
39394
|
function writeBenchmarkResult(context, result, json2) {
|
|
39284
39395
|
if (json2) {
|