terminalhire 0.42.21 → 0.42.23

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.
@@ -1893,11 +1893,11 @@ function parseGitHubRef(url) {
1893
1893
  kind: m[3] === "pull" ? "pull" : "issue"
1894
1894
  };
1895
1895
  }
1896
- async function ghGraphQL(query, variables, token, signal, governor) {
1896
+ async function ghGraphQL(query2, variables, token, signal, governor) {
1897
1897
  const init = {
1898
1898
  method: "POST",
1899
1899
  headers: { ...ghHeaders(token), "Content-Type": "application/json" },
1900
- body: JSON.stringify({ query, variables }),
1900
+ body: JSON.stringify({ query: query2, variables }),
1901
1901
  signal
1902
1902
  };
1903
1903
  if (governor) {
@@ -12408,8 +12408,8 @@ var init_stringify = __esm({
12408
12408
  });
12409
12409
 
12410
12410
  // ../../node_modules/@anthropic-ai/sdk/internal/utils/query.mjs
12411
- function stringifyQuery(query) {
12412
- return stringify(query, { arrayFormat: "brackets" });
12411
+ function stringifyQuery(query2) {
12412
+ return stringify(query2, { arrayFormat: "brackets" });
12413
12413
  }
12414
12414
  var init_query = __esm({
12415
12415
  "../../node_modules/@anthropic-ai/sdk/internal/utils/query.mjs"() {
@@ -14493,9 +14493,9 @@ var init_deployment_runs = __esm({
14493
14493
  * ```
14494
14494
  */
14495
14495
  list(params = {}, options) {
14496
- const { betas, ...query } = params ?? {};
14496
+ const { betas, ...query2 } = params ?? {};
14497
14497
  return this._client.getAPIList("/v1/deployment_runs?beta=true", PageCursor, {
14498
- query,
14498
+ query: query2,
14499
14499
  ...options,
14500
14500
  headers: buildHeaders([
14501
14501
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -14607,9 +14607,9 @@ var init_deployments = __esm({
14607
14607
  * ```
14608
14608
  */
14609
14609
  list(params = {}, options) {
14610
- const { betas, ...query } = params ?? {};
14610
+ const { betas, ...query2 } = params ?? {};
14611
14611
  return this._client.getAPIList("/v1/deployments?beta=true", PageCursor, {
14612
- query,
14612
+ query: query2,
14613
14613
  ...options,
14614
14614
  headers: buildHeaders([
14615
14615
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -14769,9 +14769,9 @@ var init_dreams = __esm({
14769
14769
  * ```
14770
14770
  */
14771
14771
  list(params = {}, options) {
14772
- const { betas, ...query } = params ?? {};
14772
+ const { betas, ...query2 } = params ?? {};
14773
14773
  return this._client.getAPIList("/v1/dreams?beta=true", PageCursor, {
14774
- query,
14774
+ query: query2,
14775
14775
  ...options,
14776
14776
  headers: buildHeaders([
14777
14777
  { "anthropic-beta": [...betas ?? [], "dreaming-2026-04-21"].toString() },
@@ -14902,9 +14902,9 @@ var init_files = __esm({
14902
14902
  * ```
14903
14903
  */
14904
14904
  list(params = {}, options) {
14905
- const { betas, ...query } = params ?? {};
14905
+ const { betas, ...query2 } = params ?? {};
14906
14906
  return this._client.getAPIList("/v1/files?beta=true", Page, {
14907
- query,
14907
+ query: query2,
14908
14908
  ...options,
14909
14909
  headers: buildHeaders([
14910
14910
  { "anthropic-beta": [...betas ?? [], "files-api-2025-04-14"].toString() },
@@ -15052,9 +15052,9 @@ var init_models = __esm({
15052
15052
  * ```
15053
15053
  */
15054
15054
  list(params = {}, options) {
15055
- const { betas, ...query } = params ?? {};
15055
+ const { betas, ...query2 } = params ?? {};
15056
15056
  return this._client.getAPIList("/v1/models?beta=true", Page, {
15057
- query,
15057
+ query: query2,
15058
15058
  ...options,
15059
15059
  headers: buildHeaders([
15060
15060
  { ...betas?.toString() != null ? { "anthropic-beta": betas?.toString() } : void 0 },
@@ -15151,9 +15151,9 @@ var init_user_profiles = __esm({
15151
15151
  * ```
15152
15152
  */
15153
15153
  list(params = {}, options) {
15154
- const { betas, ...query } = params ?? {};
15154
+ const { betas, ...query2 } = params ?? {};
15155
15155
  return this._client.getAPIList("/v1/user_profiles?beta=true", PageCursor, {
15156
- query,
15156
+ query: query2,
15157
15157
  ...options,
15158
15158
  headers: buildHeaders([
15159
15159
  { "anthropic-beta": [...betas ?? [], "user-profiles-2026-03-24"].toString() },
@@ -16012,9 +16012,9 @@ var init_versions = __esm({
16012
16012
  * ```
16013
16013
  */
16014
16014
  list(agentID, params = {}, options) {
16015
- const { betas, ...query } = params ?? {};
16015
+ const { betas, ...query2 } = params ?? {};
16016
16016
  return this._client.getAPIList(path`/v1/agents/${agentID}/versions?beta=true`, PageCursor, {
16017
- query,
16017
+ query: query2,
16018
16018
  ...options,
16019
16019
  headers: buildHeaders([
16020
16020
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -16077,9 +16077,9 @@ var init_agents = __esm({
16077
16077
  * ```
16078
16078
  */
16079
16079
  retrieve(agentID, params = {}, options) {
16080
- const { betas, ...query } = params ?? {};
16080
+ const { betas, ...query2 } = params ?? {};
16081
16081
  return this._client.get(path`/v1/agents/${agentID}?beta=true`, {
16082
- query,
16082
+ query: query2,
16083
16083
  ...options,
16084
16084
  headers: buildHeaders([
16085
16085
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -16122,9 +16122,9 @@ var init_agents = __esm({
16122
16122
  * ```
16123
16123
  */
16124
16124
  list(params = {}, options) {
16125
- const { betas, ...query } = params ?? {};
16125
+ const { betas, ...query2 } = params ?? {};
16126
16126
  return this._client.getAPIList("/v1/agents?beta=true", PageCursor, {
16127
- query,
16127
+ query: query2,
16128
16128
  ...options,
16129
16129
  headers: buildHeaders([
16130
16130
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -18214,9 +18214,9 @@ var init_work = __esm({
18214
18214
  * ```
18215
18215
  */
18216
18216
  list(environmentID, params = {}, options) {
18217
- const { betas, ...query } = params ?? {};
18217
+ const { betas, ...query2 } = params ?? {};
18218
18218
  return this._client.getAPIList(path`/v1/environments/${environmentID}/work?beta=true`, PageCursor, {
18219
- query,
18219
+ query: query2,
18220
18220
  ...options,
18221
18221
  headers: buildHeaders([
18222
18222
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -18295,9 +18295,9 @@ var init_work = __esm({
18295
18295
  * ```
18296
18296
  */
18297
18297
  poll(environmentID, params = {}, options) {
18298
- const { betas, "Anthropic-Worker-ID": anthropicWorkerID, ...query } = params ?? {};
18298
+ const { betas, "Anthropic-Worker-ID": anthropicWorkerID, ...query2 } = params ?? {};
18299
18299
  return this._client.get(path`/v1/environments/${environmentID}/work/poll?beta=true`, {
18300
- query,
18300
+ query: query2,
18301
18301
  ...options,
18302
18302
  headers: buildHeaders([
18303
18303
  {
@@ -18494,9 +18494,9 @@ var init_environments = __esm({
18494
18494
  * ```
18495
18495
  */
18496
18496
  list(params = {}, options) {
18497
- const { betas, ...query } = params ?? {};
18497
+ const { betas, ...query2 } = params ?? {};
18498
18498
  return this._client.getAPIList("/v1/environments?beta=true", PageCursor, {
18499
- query,
18499
+ query: query2,
18500
18500
  ...options,
18501
18501
  headers: buildHeaders([
18502
18502
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -18599,9 +18599,9 @@ var init_memories = __esm({
18599
18599
  * ```
18600
18600
  */
18601
18601
  retrieve(memoryID, params, options) {
18602
- const { memory_store_id, betas, ...query } = params;
18602
+ const { memory_store_id, betas, ...query2 } = params;
18603
18603
  return this._client.get(path`/v1/memory_stores/${memory_store_id}/memories/${memoryID}?beta=true`, {
18604
- query,
18604
+ query: query2,
18605
18605
  ...options,
18606
18606
  headers: buildHeaders([
18607
18607
  { "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
@@ -18647,9 +18647,9 @@ var init_memories = __esm({
18647
18647
  * ```
18648
18648
  */
18649
18649
  list(memoryStoreID, params = {}, options) {
18650
- const { betas, ...query } = params ?? {};
18650
+ const { betas, ...query2 } = params ?? {};
18651
18651
  return this._client.getAPIList(path`/v1/memory_stores/${memoryStoreID}/memories?beta=true`, PageCursor, {
18652
- query,
18652
+ query: query2,
18653
18653
  ...options,
18654
18654
  headers: buildHeaders([
18655
18655
  { "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
@@ -18707,9 +18707,9 @@ var init_memory_versions = __esm({
18707
18707
  * ```
18708
18708
  */
18709
18709
  retrieve(memoryVersionID, params, options) {
18710
- const { memory_store_id, betas, ...query } = params;
18710
+ const { memory_store_id, betas, ...query2 } = params;
18711
18711
  return this._client.get(path`/v1/memory_stores/${memory_store_id}/memory_versions/${memoryVersionID}?beta=true`, {
18712
- query,
18712
+ query: query2,
18713
18713
  ...options,
18714
18714
  headers: buildHeaders([
18715
18715
  { "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
@@ -18731,9 +18731,9 @@ var init_memory_versions = __esm({
18731
18731
  * ```
18732
18732
  */
18733
18733
  list(memoryStoreID, params = {}, options) {
18734
- const { betas, ...query } = params ?? {};
18734
+ const { betas, ...query2 } = params ?? {};
18735
18735
  return this._client.getAPIList(path`/v1/memory_stores/${memoryStoreID}/memory_versions?beta=true`, PageCursor, {
18736
- query,
18736
+ query: query2,
18737
18737
  ...options,
18738
18738
  headers: buildHeaders([
18739
18739
  { "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
@@ -18859,9 +18859,9 @@ var init_memory_stores = __esm({
18859
18859
  * ```
18860
18860
  */
18861
18861
  list(params = {}, options) {
18862
- const { betas, ...query } = params ?? {};
18862
+ const { betas, ...query2 } = params ?? {};
18863
18863
  return this._client.getAPIList("/v1/memory_stores?beta=true", PageCursor, {
18864
- query,
18864
+ query: query2,
18865
18865
  ...options,
18866
18866
  headers: buildHeaders([
18867
18867
  { "anthropic-beta": [...betas ?? [], "agent-memory-2026-07-22"].toString() },
@@ -19059,9 +19059,9 @@ var init_batches = __esm({
19059
19059
  * ```
19060
19060
  */
19061
19061
  list(params = {}, options) {
19062
- const { betas, ...query } = params ?? {};
19062
+ const { betas, ...query2 } = params ?? {};
19063
19063
  return this._client.getAPIList("/v1/messages/batches?beta=true", Page, {
19064
- query,
19064
+ query: query2,
19065
19065
  ...options,
19066
19066
  headers: buildHeaders([
19067
19067
  { "anthropic-beta": [...betas ?? [], "message-batches-2024-09-24"].toString() },
@@ -20734,9 +20734,9 @@ var init_events = __esm({
20734
20734
  * ```
20735
20735
  */
20736
20736
  list(sessionID, params = {}, options) {
20737
- const { betas, ...query } = params ?? {};
20737
+ const { betas, ...query2 } = params ?? {};
20738
20738
  return this._client.getAPIList(path`/v1/sessions/${sessionID}/events?beta=true`, PageCursor, {
20739
- query,
20739
+ query: query2,
20740
20740
  ...options,
20741
20741
  headers: buildHeaders([
20742
20742
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -20791,9 +20791,9 @@ var init_events = __esm({
20791
20791
  * ```
20792
20792
  */
20793
20793
  stream(sessionID, params = {}, options) {
20794
- const { betas, ...query } = params ?? {};
20794
+ const { betas, ...query2 } = params ?? {};
20795
20795
  return this._client.get(path`/v1/sessions/${sessionID}/events/stream?beta=true`, {
20796
- query,
20796
+ query: query2,
20797
20797
  ...options,
20798
20798
  headers: buildHeaders([
20799
20799
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -20901,9 +20901,9 @@ var init_resources = __esm({
20901
20901
  * ```
20902
20902
  */
20903
20903
  list(sessionID, params = {}, options) {
20904
- const { betas, ...query } = params ?? {};
20904
+ const { betas, ...query2 } = params ?? {};
20905
20905
  return this._client.getAPIList(path`/v1/sessions/${sessionID}/resources?beta=true`, PageCursor, {
20906
- query,
20906
+ query: query2,
20907
20907
  ...options,
20908
20908
  headers: buildHeaders([
20909
20909
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -20988,9 +20988,9 @@ var init_events2 = __esm({
20988
20988
  * ```
20989
20989
  */
20990
20990
  list(threadID, params, options) {
20991
- const { session_id, betas, ...query } = params;
20991
+ const { session_id, betas, ...query2 } = params;
20992
20992
  return this._client.getAPIList(path`/v1/sessions/${session_id}/threads/${threadID}/events?beta=true`, PageCursor, {
20993
- query,
20993
+ query: query2,
20994
20994
  ...options,
20995
20995
  headers: buildHeaders([
20996
20996
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -21077,9 +21077,9 @@ var init_threads = __esm({
21077
21077
  * ```
21078
21078
  */
21079
21079
  list(sessionID, params = {}, options) {
21080
- const { betas, ...query } = params ?? {};
21080
+ const { betas, ...query2 } = params ?? {};
21081
21081
  return this._client.getAPIList(path`/v1/sessions/${sessionID}/threads?beta=true`, PageCursor, {
21082
- query,
21082
+ query: query2,
21083
21083
  ...options,
21084
21084
  headers: buildHeaders([
21085
21085
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -21214,9 +21214,9 @@ var init_sessions = __esm({
21214
21214
  * ```
21215
21215
  */
21216
21216
  list(params = {}, options) {
21217
- const { betas, ...query } = params ?? {};
21217
+ const { betas, ...query2 } = params ?? {};
21218
21218
  return this._client.getAPIList("/v1/sessions?beta=true", BidirectionalPageCursor, {
21219
- query,
21219
+ query: query2,
21220
21220
  ...options,
21221
21221
  headers: buildHeaders([
21222
21222
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -21341,9 +21341,9 @@ var init_versions2 = __esm({
21341
21341
  * ```
21342
21342
  */
21343
21343
  list(skillID, params = {}, options) {
21344
- const { betas, ...query } = params ?? {};
21344
+ const { betas, ...query2 } = params ?? {};
21345
21345
  return this._client.getAPIList(path`/v1/skills/${skillID}/versions?beta=true`, PageCursor, {
21346
- query,
21346
+ query: query2,
21347
21347
  ...options,
21348
21348
  headers: buildHeaders([
21349
21349
  { "anthropic-beta": [...betas ?? [], "skills-2025-10-02"].toString() },
@@ -21472,9 +21472,9 @@ var init_skills2 = __esm({
21472
21472
  * ```
21473
21473
  */
21474
21474
  list(params = {}, options) {
21475
- const { betas, ...query } = params ?? {};
21475
+ const { betas, ...query2 } = params ?? {};
21476
21476
  return this._client.getAPIList("/v1/skills?beta=true", PageCursor, {
21477
- query,
21477
+ query: query2,
21478
21478
  ...options,
21479
21479
  headers: buildHeaders([
21480
21480
  { "anthropic-beta": [...betas ?? [], "skills-2025-10-02"].toString() },
@@ -21592,9 +21592,9 @@ var init_certificates = __esm({
21592
21592
  * ```
21593
21593
  */
21594
21594
  list(tunnelID, params = {}, options) {
21595
- const { betas, ...query } = params ?? {};
21595
+ const { betas, ...query2 } = params ?? {};
21596
21596
  return this._client.getAPIList(path`/v1/tunnels/${tunnelID}/certificates?beta=true`, PageCursor, {
21597
- query,
21597
+ query: query2,
21598
21598
  ...options,
21599
21599
  headers: buildHeaders([
21600
21600
  { "anthropic-beta": [...betas ?? [], "mcp-tunnels-2026-06-22"].toString() },
@@ -21721,9 +21721,9 @@ var init_tunnels = __esm({
21721
21721
  * ```
21722
21722
  */
21723
21723
  list(params = {}, options) {
21724
- const { betas, ...query } = params ?? {};
21724
+ const { betas, ...query2 } = params ?? {};
21725
21725
  return this._client.getAPIList("/v1/tunnels?beta=true", PageCursor, {
21726
- query,
21726
+ query: query2,
21727
21727
  ...options,
21728
21728
  headers: buildHeaders([
21729
21729
  { "anthropic-beta": [...betas ?? [], "mcp-tunnels-2026-06-22"].toString() },
@@ -21917,9 +21917,9 @@ var init_credentials2 = __esm({
21917
21917
  * ```
21918
21918
  */
21919
21919
  list(vaultID, params = {}, options) {
21920
- const { betas, ...query } = params ?? {};
21920
+ const { betas, ...query2 } = params ?? {};
21921
21921
  return this._client.getAPIList(path`/v1/vaults/${vaultID}/credentials?beta=true`, PageCursor, {
21922
- query,
21922
+ query: query2,
21923
21923
  ...options,
21924
21924
  headers: buildHeaders([
21925
21925
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -22090,9 +22090,9 @@ var init_vaults = __esm({
22090
22090
  * ```
22091
22091
  */
22092
22092
  list(params = {}, options) {
22093
- const { betas, ...query } = params ?? {};
22093
+ const { betas, ...query2 } = params ?? {};
22094
22094
  return this._client.getAPIList("/v1/vaults?beta=true", PageCursor, {
22095
- query,
22095
+ query: query2,
22096
22096
  ...options,
22097
22097
  headers: buildHeaders([
22098
22098
  { "anthropic-beta": [...betas ?? [], "managed-agents-2026-04-01"].toString() },
@@ -22978,8 +22978,8 @@ var init_batches2 = __esm({
22978
22978
  * }
22979
22979
  * ```
22980
22980
  */
22981
- list(query = {}, options) {
22982
- return this._client.getAPIList("/v1/messages/batches", Page, { query, ...options });
22981
+ list(query2 = {}, options) {
22982
+ return this._client.getAPIList("/v1/messages/batches", Page, { query: query2, ...options });
22983
22983
  }
22984
22984
  /**
22985
22985
  * Delete a Message Batch.
@@ -23235,9 +23235,9 @@ var init_models2 = __esm({
23235
23235
  * use in the API. More recently released models are listed first.
23236
23236
  */
23237
23237
  list(params = {}, options) {
23238
- const { betas, ...query } = params ?? {};
23238
+ const { betas, ...query2 } = params ?? {};
23239
23239
  return this._client.getAPIList("/v1/models", Page, {
23240
- query,
23240
+ query: query2,
23241
23241
  ...options,
23242
23242
  headers: buildHeaders([
23243
23243
  { ...betas?.toString() != null ? { "anthropic-beta": betas?.toString() } : void 0 },
@@ -23586,8 +23586,8 @@ var init_client = __esm({
23586
23586
  }
23587
23587
  return buildHeaders([{ Authorization: `Bearer ${this.authToken}` }]);
23588
23588
  }
23589
- stringifyQuery(query) {
23590
- return stringifyQuery(query);
23589
+ stringifyQuery(query2) {
23590
+ return stringifyQuery(query2);
23591
23591
  }
23592
23592
  getUserAgent() {
23593
23593
  return `${this.constructor.name}/JS ${VERSION}`;
@@ -23598,16 +23598,16 @@ var init_client = __esm({
23598
23598
  makeStatusError(status, error, message, headers) {
23599
23599
  return APIError.generate(status, error, message, headers);
23600
23600
  }
23601
- buildURL(path5, query, defaultBaseURL) {
23601
+ buildURL(path5, query2, defaultBaseURL) {
23602
23602
  const baseURL = !__classPrivateFieldGet(this, _BaseAnthropic_instances, "m", _BaseAnthropic_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
23603
23603
  const url = isAbsoluteURL(path5) ? new URL(path5) : new URL(baseURL + (baseURL.endsWith("/") && path5.startsWith("/") ? path5.slice(1) : path5));
23604
23604
  const defaultQuery = this.defaultQuery();
23605
23605
  const pathQuery = Object.fromEntries(url.searchParams);
23606
23606
  if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
23607
- query = { ...pathQuery, ...defaultQuery, ...query };
23607
+ query2 = { ...pathQuery, ...defaultQuery, ...query2 };
23608
23608
  }
23609
- if (typeof query === "object" && query && !Array.isArray(query)) {
23610
- url.search = this.stringifyQuery(query);
23609
+ if (typeof query2 === "object" && query2 && !Array.isArray(query2)) {
23610
+ url.search = this.stringifyQuery(query2);
23611
23611
  }
23612
23612
  return url.toString();
23613
23613
  }
@@ -23920,14 +23920,14 @@ var init_client = __esm({
23920
23920
  }
23921
23921
  async buildRequest(inputOptions, { retryCount = 0 } = {}) {
23922
23922
  const options = { ...inputOptions };
23923
- const { method, path: path5, query, defaultBaseURL } = options;
23923
+ const { method, path: path5, query: query2, defaultBaseURL } = options;
23924
23924
  if (this._authState.resolution) {
23925
23925
  await this._authState.resolution;
23926
23926
  }
23927
23927
  if (!this._baseURLIsExplicit && this._authState.baseURL && this.baseURL !== this._authState.baseURL) {
23928
23928
  this.baseURL = this._authState.baseURL;
23929
23929
  }
23930
- const url = this.buildURL(path5, query, defaultBaseURL);
23930
+ const url = this.buildURL(path5, query2, defaultBaseURL);
23931
23931
  if ("timeout" in options)
23932
23932
  validatePositiveInteger("timeout", options.timeout);
23933
23933
  options.timeout = options.timeout ?? this.timeout;
@@ -24045,8 +24045,8 @@ function stripFallbackBlocks(body) {
24045
24045
  function betaRefusalFallbackMiddleware(fallbacks, options = {}) {
24046
24046
  let warnedMissingState = false;
24047
24047
  return async (request, next, ctx) => {
24048
- const [path5, query] = (ctx.options?.path ?? "").split("?");
24049
- if (fallbacks.length === 0 || ctx.options?.method !== "post" || path5 !== "/v1/messages" || new URLSearchParams(query).get("beta") !== "true" || typeof ctx.options.body !== "object" || ctx.options.body == null) {
24048
+ const [path5, query2] = (ctx.options?.path ?? "").split("?");
24049
+ if (fallbacks.length === 0 || ctx.options?.method !== "post" || path5 !== "/v1/messages" || new URLSearchParams(query2).get("beta") !== "true" || typeof ctx.options.body !== "object" || ctx.options.body == null) {
24050
24050
  return next(request);
24051
24051
  }
24052
24052
  if (ctx.options.body.fallbacks != null) {
@@ -26050,7 +26050,7 @@ ${facts.stderr}`))
26050
26050
  return null;
26051
26051
  }
26052
26052
  function isCommandUnavailable(facts, countsRead) {
26053
- if (facts.exitCode === 127 || facts.exitCode === 126)
26053
+ if (facts.exitCode === 127 || facts.exitCode === 126 || facts.exitCode === 125)
26054
26054
  return true;
26055
26055
  if (countsRead)
26056
26056
  return false;
@@ -26058,6 +26058,22 @@ function isCommandUnavailable(facts, countsRead) {
26058
26058
  return false;
26059
26059
  return facts.stdout.trim().length === 0 && EXEC_FAILURE.test(facts.stderr);
26060
26060
  }
26061
+ function isOfflineBuildGap(facts, counts) {
26062
+ if (facts.runtime !== "jvm")
26063
+ return null;
26064
+ if (counts !== null && counts.tests_failed > 0)
26065
+ return null;
26066
+ if (facts.exitCode === 0 || facts.exitCode === null)
26067
+ return null;
26068
+ const out = `${facts.stdout}
26069
+ ${facts.stderr}`;
26070
+ for (const pattern of OFFLINE_BUILD_GAP) {
26071
+ const hit = pattern.exec(out);
26072
+ if (hit)
26073
+ return hit[0];
26074
+ }
26075
+ return null;
26076
+ }
26061
26077
  function classifyVerification(facts) {
26062
26078
  if (facts.timedOut) {
26063
26079
  return {
@@ -26082,6 +26098,14 @@ function classifyVerification(facts) {
26082
26098
  reason: `the test command could not be invoked (exit ${String(facts.exitCode)}). This is an environment failure on our side \u2014 a missing interpreter, a binary outside PATH, or a dependency installed somewhere the command cannot see it. The repo has not been judged.`
26083
26099
  };
26084
26100
  }
26101
+ const gap = isOfflineBuildGap(facts, counts);
26102
+ if (gap !== null) {
26103
+ return {
26104
+ outcome: "test-command-unavailable",
26105
+ counts,
26106
+ reason: `the build stopped on something our install step did not fetch (${gap}). The test step has no network, so this is a gap in our environment, not a failure in the repo. The repo has not been judged.`
26107
+ };
26108
+ }
26085
26109
  if (counts !== null) {
26086
26110
  if (counts.tests_failed > 0) {
26087
26111
  return {
@@ -26136,10 +26160,19 @@ function isGreen(outcome) {
26136
26160
  function isOurFault(outcome) {
26137
26161
  return outcome === "test-command-unavailable" || outcome === "counts-unparsed";
26138
26162
  }
26139
- var int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE;
26163
+ var VERIFICATION_OUTCOMES, int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, OFFLINE_BUILD_GAP;
26140
26164
  var init_classify2 = __esm({
26141
26165
  "../../packages/envrun/dist/classify.js"() {
26142
26166
  "use strict";
26167
+ VERIFICATION_OUTCOMES = [
26168
+ "completed",
26169
+ "tests-failed",
26170
+ "no-tests-observed",
26171
+ "counts-unparsed",
26172
+ "test-command-unavailable",
26173
+ "environment-exhausted",
26174
+ "budget-exceeded"
26175
+ ];
26143
26176
  int = (m, i = 1) => m ? Number(m[i]) : 0;
26144
26177
  withSuiteFailures = (counts, suiteLine) => {
26145
26178
  if (counts.tests_failed > 0 || !suiteLine)
@@ -26239,6 +26272,51 @@ var init_classify2 = __esm({
26239
26272
  return { tests_passed: int(passed), tests_failed: int(failed) + int(errors) };
26240
26273
  }
26241
26274
  },
26275
+ {
26276
+ // Maven surefire's per-module summary (TERM-1122), measured against surefire
26277
+ // 3.2.5 through `th run`:
26278
+ //
26279
+ // [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
26280
+ //
26281
+ // The per-CLASS lines carry the same counts followed by `, Time elapsed: …`, so
26282
+ // the end anchor after `Skipped` is what keeps them out; counting both would
26283
+ // double every test. One summary prints per module, so they are SUMMED. An
26284
+ // error is a test that could not run to an assertion, counted as failed so a
26285
+ // green verdict is impossible while one is outstanding, as pytest's are.
26286
+ runner: "maven surefire",
26287
+ read: (out) => {
26288
+ const lines = [
26289
+ ...out.matchAll(/^(?:\[(?:INFO|WARNING|ERROR)\] )?Tests run: (\d+), Failures: (\d+), Errors: (\d+), Skipped: (\d+)(?:, Flakes: \d+)?\s*$/gm)
26290
+ ];
26291
+ if (lines.length === 0)
26292
+ return null;
26293
+ let passed = 0;
26294
+ let failed = 0;
26295
+ for (const m of lines) {
26296
+ const [run2, failures, errors, skipped] = [m[1], m[2], m[3], m[4]].map(Number);
26297
+ failed += failures + errors;
26298
+ passed += Math.max(0, run2 - failures - errors - skipped);
26299
+ }
26300
+ return { tests_passed: passed, tests_failed: failed };
26301
+ }
26302
+ },
26303
+ {
26304
+ // Gradle prints no test counts when every test passes, so envrun installs an
26305
+ // init script that prints one line per Test task (`gradleTestSummary.ts`,
26306
+ // TERM-1122). The marker is ours; its format is fixed there.
26307
+ runner: "gradle",
26308
+ read: (out) => {
26309
+ const lines = [
26310
+ ...out.matchAll(/^terminalhire-gradle-tests: task=\S+ total=(\d+) passed=(\d+) failed=(\d+) skipped=(\d+)\s*$/gm)
26311
+ ];
26312
+ if (lines.length === 0)
26313
+ return null;
26314
+ return {
26315
+ tests_passed: lines.reduce((sum, m) => sum + Number(m[2]), 0),
26316
+ tests_failed: lines.reduce((sum, m) => sum + Number(m[3]), 0)
26317
+ };
26318
+ }
26319
+ },
26242
26320
  {
26243
26321
  // mocha: ` 440 passing (1s)` and ` 2 failing`
26244
26322
  runner: "mocha",
@@ -26273,6 +26351,79 @@ var init_classify2 = __esm({
26273
26351
  const p = int(passed);
26274
26352
  return { tests_passed: p, tests_failed: Math.max(0, t - p) };
26275
26353
  }
26354
+ },
26355
+ {
26356
+ /**
26357
+ * BARE TAP — assertion lines with no plan and no summary comment (TERM-1138).
26358
+ *
26359
+ * LAST ON PURPOSE. It is the loosest reader here, so anything that a more specific
26360
+ * one can read must reach that one first: `node --test` and node-tap both emit `ok`
26361
+ * lines AND a closing summary, and their readers are ordered above for that reason.
26362
+ * Moving this up would have it answer for both.
26363
+ *
26364
+ * A hand-rolled harness prints neither summary — it emits its assertions and stops.
26365
+ * That is what morebetterltd/wheelhouse does: thirty `ok N - …` lines, exit 0, and
26366
+ * before this every one of those runs reported `counts-unparsed`, which the claim
26367
+ * page renders as "RESULT UNREADABLE — THE SUITE EXITED 0" over a suite that passed.
26368
+ *
26369
+ * ON THE MISSING PLAN LINE. TAP requires `1..N` so a reader can tell a finished
26370
+ * stream from a truncated one, and there is none here, so this cannot prove the
26371
+ * suite reached its end. That is a real limit and it is NOT a reason to refuse,
26372
+ * because it holds this format to a standard no reader in this file meets — the
26373
+ * jest reader greps a line out of stdout that a repository could `echo` without
26374
+ * running anything. These are format adapters, not a trust boundary; the boundary
26375
+ * is that the command is the repository's own or is refused a signature.
26376
+ *
26377
+ * CONTIGUOUS FROM 1, which buys self-consistency rather than security:
26378
+ *
26379
+ * - starting at 1 rejects a TRUNCATION. `testOutputTail` keeps the last lines, so
26380
+ * a stream beginning `ok 47` is the middle of a longer run and counting it would
26381
+ * report three tests for a suite that ran fifty.
26382
+ * - no gaps rejects prose that happens to carry the word, and a stream interleaved
26383
+ * with another's.
26384
+ * - anchored with no leading space rejects TAP SUBTESTS, which indent. Counting
26385
+ * those would double them against their parent.
26386
+ */
26387
+ runner: "bare TAP",
26388
+ read: (out) => {
26389
+ let n = 0;
26390
+ let failed = 0;
26391
+ let passed = 0;
26392
+ let plan = null;
26393
+ for (const line of out.split("\n")) {
26394
+ if (/^Bail out!/.test(line))
26395
+ return null;
26396
+ const p = /^(\d+)\.\.(\d+)\s*$/.exec(line);
26397
+ if (p) {
26398
+ const declared = Number(p[2]) - Number(p[1]) + 1;
26399
+ if (plan !== null && plan !== declared)
26400
+ return null;
26401
+ plan = declared;
26402
+ continue;
26403
+ }
26404
+ const m = /^(not )?ok (\d+)(?:\s|$)/.exec(line);
26405
+ if (!m)
26406
+ continue;
26407
+ if (Number(m[2]) !== n + 1)
26408
+ return null;
26409
+ n += 1;
26410
+ const directive = /#\s*(skip|todo)\b/i.exec(line);
26411
+ const kind = directive?.[1]?.toLowerCase();
26412
+ if (kind === "skip")
26413
+ continue;
26414
+ if (m[1] !== void 0) {
26415
+ if (kind !== "todo")
26416
+ failed += 1;
26417
+ continue;
26418
+ }
26419
+ passed += 1;
26420
+ }
26421
+ if (n === 0)
26422
+ return null;
26423
+ if (plan !== null && plan !== n)
26424
+ return null;
26425
+ return { tests_passed: passed, tests_failed: failed };
26426
+ }
26276
26427
  }
26277
26428
  ];
26278
26429
  SUPPORTED_RUNNERS = READERS.map((r) => r.runner);
@@ -26294,6 +26445,21 @@ var init_classify2 = __esm({
26294
26445
  "\\d+\\s+examples?,\\s+(?!0\\b)\\d+\\s+failures?"
26295
26446
  // rspec's summary line
26296
26447
  ].join("|"), "m");
26448
+ OFFLINE_BUILD_GAP = [
26449
+ // Maven, `-o`: `Cannot access central (…) in offline mode and the artifact … has
26450
+ // not been downloaded from it before.`
26451
+ /in offline mode and the artifact \S+ has not been downloaded from it before/,
26452
+ // Maven without `-o`, as a CI-derived `mvn test` runs: it tries the network.
26453
+ /Could not transfer artifact \S+ from\/to /,
26454
+ /Plugin \S+ or one of its dependencies could not be resolved/,
26455
+ // Gradle, `--offline`.
26456
+ /No cached version (?:listing )?(?:of|for) \S+ available for offline mode/,
26457
+ // Gradle without `--offline`, and the wrapper fetching its own distribution.
26458
+ /Could not GET 'https?:\/\//,
26459
+ /^Downloading https:\/\/services\.gradle\.org\/distributions\//m,
26460
+ /No matching toolchains found for requested specification/,
26461
+ /Cannot find a Java installation on your machine matching/
26462
+ ];
26297
26463
  }
26298
26464
  });
26299
26465
 
@@ -26359,6 +26525,12 @@ function scrubEnv(source, opts) {
26359
26525
  env["GOCACHE"] = join18(jailHome, ".cache", "go-build");
26360
26526
  env["GOFLAGS"] = "-modcacherw";
26361
26527
  env["CARGO_HOME"] = join18(jailHome, ".cargo");
26528
+ env["MAVEN_OPTS"] = `-Dmaven.repo.local=${join18(jailHome, ".m2", "repository")}`;
26529
+ env["GRADLE_USER_HOME"] = join18(jailHome, ".gradle");
26530
+ const bundle = join18(jailHome, ".bundle");
26531
+ env["GEM_HOME"] = bundle;
26532
+ env["BUNDLE_PATH"] = bundle;
26533
+ env["BUNDLE_APP_CONFIG"] = bundle;
26362
26534
  const proxy = opts.proxyUrl ?? DEAD_PROXY;
26363
26535
  env["HTTP_PROXY"] = proxy;
26364
26536
  env["HTTPS_PROXY"] = proxy;
@@ -26659,6 +26831,31 @@ var init_egressProxy = __esm({
26659
26831
  "static.crates.io",
26660
26832
  "index.crates.io",
26661
26833
  "crates.io",
26834
+ // TERM-1139. `ruby` has had an image since the mapping existed and `bundle install`
26835
+ // as its derived command, and this host was missing — so every ruby repo could start
26836
+ // and could never install. Install is the ONLY step with a network grant at all, so a
26837
+ // denial here is terminal, not a slow path.
26838
+ //
26839
+ // One entry, not two: `hostAllowed` is dot-anchored SUFFIX matching, so this covers
26840
+ // `index.rubygems.org` as well. Listing that separately would advertise the grant as
26841
+ // narrower than it is, which the `github.com` note below rejects for the same reason.
26842
+ "rubygems.org",
26843
+ // TERM-1139, added with the `dotnet` image mapping. `dotnet restore` fetches from
26844
+ // NuGet; the coupling test refuses a mapped runtime whose registry is unreachable, and
26845
+ // it caught this one the moment the image landed.
26846
+ "api.nuget.org",
26847
+ // TERM-1122, added with the `jvm` image mapping, each from a MEASURED denial
26848
+ // through `th run` (the proxy's 403 in the build's own output), not a guess:
26849
+ // - Maven Central, where both `mvn` and Gradle's `mavenCentral()` resolve.
26850
+ // - `services.gradle.org`, where a Gradle wrapper fetches its distribution.
26851
+ // - `release-assets.githubusercontent.com`, where that fetch LANDS. Measured
26852
+ // chain: services.gradle.org 307 -> github.com 302 -> this host 200. The
26853
+ // `github.com` note below keeps `githubusercontent.com` denied until a real
26854
+ // install needs it; this is that install. Only this one host: raw blobs and
26855
+ // `objects.githubusercontent.com` stay denied.
26856
+ "repo.maven.apache.org",
26857
+ "services.gradle.org",
26858
+ "release-assets.githubusercontent.com",
26662
26859
  // Git dependencies. Approved by Eric 2026-07-22 after a live run measured the
26663
26860
  // chokepoint working under load (20 events, 16 allowed / 4 denied by host).
26664
26861
  //
@@ -26950,6 +27147,9 @@ function containerArgs(spec, env, opts) {
26950
27147
  args.push(`--env=${k}=${url}`);
26951
27148
  }
26952
27149
  args.push("--env=NO_PROXY=", "--env=no_proxy=");
27150
+ const host = opts.net.proxyHost;
27151
+ const port = String(opts.net.proxyPort);
27152
+ args.push(`--env=JAVA_TOOL_OPTIONS=-Dhttp.proxyHost=${host} -Dhttp.proxyPort=${port} -Dhttps.proxyHost=${host} -Dhttps.proxyPort=${port} -Daether.connector.http.useSystemProperties=true`);
26953
27153
  }
26954
27154
  args.push("--");
26955
27155
  args.push(validateImage(opts.image ?? DEFAULT_CONTAINER_IMAGE));
@@ -27282,7 +27482,10 @@ var init_container = __esm({
27282
27482
  "http_proxy",
27283
27483
  "https_proxy",
27284
27484
  "NO_PROXY",
27285
- "no_proxy"
27485
+ "no_proxy",
27486
+ // Carries the JVM's copy of the proxy (below), so an inherited value is dropped
27487
+ // for the same reason as the rest.
27488
+ "JAVA_TOOL_OPTIONS"
27286
27489
  ]);
27287
27490
  WINDOWS_DRIVE_ROOT = /^[A-Za-z]:[\\/]/;
27288
27491
  IMAGE_HOST = "(?:[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)+(?::[0-9]+)?|localhost(?::[0-9]+)?)";
@@ -27332,11 +27535,17 @@ var init_dist = __esm({
27332
27535
  function censusTotal(c) {
27333
27536
  return c.containers.length + c.volumes.length + c.networks.length;
27334
27537
  }
27335
- function ids(docker3, args) {
27538
+ function query(docker3, args) {
27336
27539
  const res = docker3.sync([...args], { timeoutMs: 15e3 });
27337
- if (res.error || res.status !== 0)
27338
- return [];
27339
- return res.stdout.split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
27540
+ if (res.error || res.status !== 0) {
27541
+ const why = res.error?.message ?? (res.stderr.trim() || `exit ${String(res.status)}`);
27542
+ return { ids: [], failure: `docker ${args.slice(0, 2).join(" ")}: ${why}` };
27543
+ }
27544
+ const ids2 = res.stdout.split("\n").map((s) => s.trim()).filter((s) => s.length > 0);
27545
+ return { ids: ids2, failure: null };
27546
+ }
27547
+ function ids(docker3, args) {
27548
+ return query(docker3, args).ids;
27340
27549
  }
27341
27550
  function census(docker3, label) {
27342
27551
  const filter = `label=${label}`;
@@ -27346,25 +27555,67 @@ function census(docker3, label) {
27346
27555
  networks: ids(docker3, ["network", "ls", "-q", "--filter", filter])
27347
27556
  };
27348
27557
  }
27558
+ function censusReport(docker3, label) {
27559
+ const filter = `label=${label}`;
27560
+ const failures = [];
27561
+ const ask2 = (args) => {
27562
+ const q = query(docker3, args);
27563
+ if (q.failure !== null)
27564
+ failures.push(q.failure);
27565
+ return q.ids;
27566
+ };
27567
+ const taken = {
27568
+ containers: ask2(["ps", "-aq", "--filter", filter]),
27569
+ volumes: ask2(["volume", "ls", "-q", "--filter", filter]),
27570
+ networks: ask2(["network", "ls", "-q", "--filter", filter])
27571
+ };
27572
+ return failures.length === 0 ? { observed: true, census: taken, unobservedReason: null } : { observed: false, census: taken, unobservedReason: failures.join("; ") };
27573
+ }
27349
27574
  function localCensus(label) {
27350
27575
  return census(localDockerClient(), label);
27351
27576
  }
27352
- function judgeLeaks(peak, after) {
27577
+ function judgeLeaks(peak, after, observation) {
27353
27578
  const labelObserved = peak.containers.length > 0;
27579
+ if (!observation.observed) {
27580
+ return {
27581
+ labelObserved,
27582
+ reaped: false,
27583
+ observed: false,
27584
+ clean: false,
27585
+ state: "unobserved",
27586
+ peak,
27587
+ after,
27588
+ note: `UNOBSERVED, not clean: we could not look at what survived teardown (${observation.unobservedReason ?? "no reason given"}), so nothing is known about leaks on this run, in either direction.`
27589
+ };
27590
+ }
27354
27591
  const reaped = censusTotal(after) === 0;
27355
27592
  let note;
27593
+ let state;
27356
27594
  if (!labelObserved && reaped) {
27595
+ state = "inconclusive";
27357
27596
  note = "INCONCLUSIVE, not clean: nothing labelled was ever seen alive, so an empty final census is equally consistent with the label never being applied. The control failed, so the denial proves nothing.";
27358
27597
  } else if (!labelObserved) {
27598
+ state = "leak";
27359
27599
  note = "no labelled container was observed alive AND objects remain \u2014 the label wiring is wrong.";
27360
27600
  } else if (!reaped) {
27601
+ state = "leak";
27361
27602
  note = `LEAK: ${String(censusTotal(after))} labelled object(s) survived teardown (containers=${String(after.containers.length)} volumes=${String(after.volumes.length)} networks=${String(after.networks.length)}).`;
27362
27603
  } else {
27604
+ state = "clean";
27363
27605
  note = `clean: peak ${String(peak.containers.length)} labelled container(s) observed alive, 0 labelled objects remain after teardown.`;
27364
27606
  }
27365
- return { labelObserved, reaped, clean: labelObserved && reaped, peak, after, note };
27607
+ return {
27608
+ labelObserved,
27609
+ reaped,
27610
+ observed: true,
27611
+ clean: labelObserved && reaped,
27612
+ state,
27613
+ peak,
27614
+ after,
27615
+ note
27616
+ };
27366
27617
  }
27367
- var RUN_LABEL_KEY, LabelWatch;
27618
+ var RUN_LABEL_KEY, LabelWatch, LEAK_STATES;
27368
27619
  var init_labels = __esm({
27369
27620
  "../../packages/envrun/dist/labels.js"() {
27370
27621
  "use strict";
@@ -27419,6 +27670,7 @@ var init_labels = __esm({
27419
27670
  return this.#samples;
27420
27671
  }
27421
27672
  };
27673
+ LEAK_STATES = ["clean", "leak", "inconclusive", "unobserved"];
27422
27674
  }
27423
27675
  });
27424
27676
 
@@ -27903,7 +28155,7 @@ function acquireLocalLease(runId) {
27903
28155
  observed: false,
27904
28156
  census: { containers: [], volumes: [], networks: [] },
27905
28157
  unobservedReason: RELEASED_LEASE_CENSUS_REASON
27906
- } : { observed: true, census: census(docker3, label), unobservedReason: null }),
28158
+ } : censusReport(docker3, label)),
27907
28159
  publishPreview: (req) => startPreview({ ...req, docker: docker3 }),
27908
28160
  release: () => {
27909
28161
  if (released) {
@@ -28023,6 +28275,17 @@ caused by: ${frame}`;
28023
28275
  }
28024
28276
  return frames === 0 ? null : out;
28025
28277
  }
28278
+ function imageVariantFor(spec) {
28279
+ if (spec.runtime !== "jvm")
28280
+ return void 0;
28281
+ const callsGradle = /(?:^|[\s;&|(])gradle(?=\s|$)/;
28282
+ return [spec.installCommand, spec.testCommand].some((c) => c != null && callsGradle.test(c)) ? "gradle" : void 0;
28283
+ }
28284
+ function shapeFor(runtime, variant) {
28285
+ if (runtime === "jvm" && variant === "gradle")
28286
+ return JVM_GRADLE_IMAGE;
28287
+ return RUNTIME_IMAGES[runtime];
28288
+ }
28026
28289
  function atLeast(a, b) {
28027
28290
  const left = a.split(".").map(Number);
28028
28291
  const right = b.split(".").map(Number);
@@ -28035,19 +28298,19 @@ function atLeast(a, b) {
28035
28298
  }
28036
28299
  return true;
28037
28300
  }
28038
- function imageForRuntime(runtime, override, version) {
28301
+ function imageForRuntime(runtime, override, version, variant) {
28039
28302
  if (override)
28040
28303
  return override;
28041
- const shape = RUNTIME_IMAGES[runtime];
28304
+ const shape = shapeFor(runtime, variant);
28042
28305
  if (!shape) {
28043
- throw new RunRefusalError(`no container image is mapped for runtime ${JSON.stringify(runtime)}. Refusing to run it in the Node image: a missing interpreter surfaces as "command not found", which the classifier reads as tests-failed \u2014 a false red blamed on the repo.`);
28306
+ throw new RunRefusalError(`no container image is mapped for runtime ${JSON.stringify(runtime)}. Refusing to run it in the Node image: a bare "command not found" exits 127, which classifyVerification already owns as ours \u2014 but a test script that RUNS and fails inside on the missing tool exits with its own status and prints to stdout, and that falls through to tests-failed \u2014 a false red blamed on the repo.`);
28044
28307
  }
28045
28308
  if (version === void 0 || version === null)
28046
28309
  return unversionedImage(shape);
28047
28310
  if (!TAG_VERSION.test(version)) {
28048
28311
  throw new RunRefusalError(`runtime version ${JSON.stringify(version)} is not a bare version, so no image tag can be built from it. Refusing rather than booting the default: the repo asked for a version, and supplying a different one silently is what TERM-643 fixed.`);
28049
28312
  }
28050
- return `${shape.repository}:${version}${shape.suffix}`;
28313
+ return `${shape.repository}:${shape.prefix ?? ""}${version}${shape.suffix}`;
28051
28314
  }
28052
28315
  function setManifestProbe(probe) {
28053
28316
  const previous = manifestProbe;
@@ -28060,10 +28323,10 @@ function imageDefinitelyAbsent(image) {
28060
28323
  return false;
28061
28324
  return /manifest unknown|no such manifest/i.test(res.output);
28062
28325
  }
28063
- function resolvePublishedImage(image, runtime, version) {
28326
+ function resolvePublishedImage(image, runtime, version, variant) {
28064
28327
  if (!imageDefinitelyAbsent(image))
28065
28328
  return image;
28066
- const shape = RUNTIME_IMAGES[runtime];
28329
+ const shape = shapeFor(runtime, variant);
28067
28330
  if (shape && shape.declaredIsFloor && atLeast(shape.defaultVersion, version)) {
28068
28331
  return unversionedImage(shape);
28069
28332
  }
@@ -28071,18 +28334,19 @@ function resolvePublishedImage(image, runtime, version) {
28071
28334
  }
28072
28335
  function resolveImageForSpec(spec, override) {
28073
28336
  const declared = spec.runtimeVersion;
28074
- const image = imageForRuntime(spec.runtime, override, declared);
28337
+ const variant = imageVariantFor(spec);
28338
+ const image = imageForRuntime(spec.runtime, override, declared, variant);
28075
28339
  const constructed = (() => {
28076
28340
  if (declared === null)
28077
28341
  return null;
28078
28342
  try {
28079
- return imageForRuntime(spec.runtime, void 0, declared);
28343
+ return imageForRuntime(spec.runtime, void 0, declared, variant);
28080
28344
  } catch {
28081
28345
  return null;
28082
28346
  }
28083
28347
  })();
28084
28348
  if (constructed !== null && image === constructed && declared !== null) {
28085
- return resolvePublishedImage(image, spec.runtime, declared);
28349
+ return resolvePublishedImage(image, spec.runtime, declared, variant);
28086
28350
  }
28087
28351
  return image;
28088
28352
  }
@@ -28109,12 +28373,22 @@ function assertVenueOwnerDeclared(lease) {
28109
28373
  return;
28110
28374
  throw new RunRefusalError("this venue did not say which account owns the tree it staged, so the guest would run under this machine's uid and could not write its own clone. We refuse rather than report that permission error as the repo's tests failing.");
28111
28375
  }
28376
+ function refuseUnbuildableSpec(spec) {
28377
+ if (spec.runtime !== "jvm" || spec.installCommand !== null || spec.testCommand !== null)
28378
+ return;
28379
+ const why = spec.unresolved.find((r) => r.kind === "no-test-command")?.detail;
28380
+ if (why === void 0) {
28381
+ throw new EnvRunError("internal: a jvm spec with no test command carries no no-test-command reason");
28382
+ }
28383
+ throw new RunRefusalError(`refusing to run this repository: ${why}.`);
28384
+ }
28112
28385
  async function runEnvironmentSpec(req) {
28113
28386
  const startedAt = Date.now();
28114
28387
  const containment = req.lease.containment;
28115
28388
  if (containment.kind !== "container") {
28116
28389
  throw new EnvRunError(`phase 2 requires the container tier, got ${containment.kind}. Refusing: a container phase that silently ran under seatbelt would make every container claim vacuous.`);
28117
28390
  }
28391
+ refuseUnbuildableSpec(req.spec);
28118
28392
  assertVenueOwnerDeclared(req.lease);
28119
28393
  const image = resolveImageForSpec(req.spec, req.image);
28120
28394
  const { jail, tmp } = req;
@@ -28157,9 +28431,10 @@ async function runEnvironmentSpec(req) {
28157
28431
  installOk: false
28158
28432
  };
28159
28433
  } else if (req.spec.testCommand === null) {
28434
+ const why = req.spec.unresolved.find((r) => r.kind === "no-test-command")?.detail;
28160
28435
  result = {
28161
28436
  outcome: "no-tests-observed",
28162
- note: "the spec derived no test command, so nothing was executed: nothing failed and nothing ran. Certain, not inferred \u2014 no command was ever invoked.",
28437
+ note: "the spec derived no test command, so nothing was executed: nothing failed and nothing ran. Certain, not inferred \u2014 no command was ever invoked." + (why === void 0 ? "" : ` Why: ${why}.`),
28163
28438
  installOk: true
28164
28439
  };
28165
28440
  } else {
@@ -28182,7 +28457,7 @@ async function runEnvironmentSpec(req) {
28182
28457
  labels,
28183
28458
  timeoutMs: req.testTimeoutMs ?? 9e5
28184
28459
  });
28185
- const verdict = classifyVerification(toExecution(test));
28460
+ const verdict = classifyVerification({ ...toExecution(test), runtime: req.spec.runtime });
28186
28461
  result = { outcome: verdict.outcome, note: verdict.reason, installOk: true };
28187
28462
  }
28188
28463
  } finally {
@@ -28196,6 +28471,10 @@ async function runEnvironmentSpec(req) {
28196
28471
  volumes: [],
28197
28472
  networks: []
28198
28473
  };
28474
+ const observation = afterReport ?? {
28475
+ observed: false,
28476
+ unobservedReason: "the run carried no label, so there was nothing to count by"
28477
+ };
28199
28478
  return {
28200
28479
  outcome: result.outcome,
28201
28480
  tier: "container",
@@ -28204,7 +28483,7 @@ async function runEnvironmentSpec(req) {
28204
28483
  test,
28205
28484
  installOk: result.installOk,
28206
28485
  counts: test ? readCounts(test.stdout, test.stderr) : null,
28207
- leaks: judgeLeaks(peak, after),
28486
+ leaks: judgeLeaks(peak, after, observation),
28208
28487
  note: result.note,
28209
28488
  wallMs: Date.now() - startedAt
28210
28489
  };
@@ -28254,7 +28533,7 @@ async function runStep(containment, r) {
28254
28533
  wallMs: Date.now() - startedAt
28255
28534
  };
28256
28535
  }
28257
- var EnvRunError, RunRefusalError, MAX_CAUSE_FRAMES, CHAIN_UNREADABLE, CHAIN_TOO_DEEP, RUNTIME_IMAGES, unversionedImage, TAG_VERSION, dockerManifestProbe, manifestProbe, MISSING_IMAGE_SHAPE;
28536
+ var EnvRunError, RunRefusalError, MAX_CAUSE_FRAMES, CHAIN_UNREADABLE, CHAIN_TOO_DEEP, RUNTIME_IMAGES, JVM_GRADLE_IMAGE, unversionedImage, TAG_VERSION, dockerManifestProbe, manifestProbe, MISSING_IMAGE_SHAPE;
28258
28537
  var init_execute = __esm({
28259
28538
  "../../packages/envrun/dist/execute.js"() {
28260
28539
  "use strict";
@@ -28291,9 +28570,66 @@ var init_execute = __esm({
28291
28570
  },
28292
28571
  go: { repository: "golang", suffix: "-bookworm", defaultVersion: "1.23", declaredIsFloor: true },
28293
28572
  ruby: { repository: "ruby", suffix: "-bookworm", defaultVersion: "3.3", declaredIsFloor: false },
28294
- rust: { repository: "rust", suffix: "-bookworm", defaultVersion: "1", declaredIsFloor: true }
28295
- };
28296
- unversionedImage = (shape) => `${shape.repository}:${shape.defaultVersion}${shape.suffix}`;
28573
+ rust: { repository: "rust", suffix: "-bookworm", defaultVersion: "1", declaredIsFloor: true },
28574
+ /**
28575
+ * TERM-1139. `dotnet` was unmapped alongside `jvm`, and the recorded reason was that
28576
+ * neither had "a single obvious base image (gradle vs maven, sdk vs runtime)". That
28577
+ * reason holds for jvm and it does NOT hold here, which is why only this half moved.
28578
+ *
28579
+ * "sdk vs runtime" answers itself: the derived commands are `dotnet restore` and
28580
+ * `dotnet test`, and neither exists on the runtime image. There is no second build
28581
+ * tool competing for the slot the way maven and gradle compete for jvm's — and since
28582
+ * no official image carries both of those, jvm genuinely cannot be served by one key.
28583
+ *
28584
+ * MEASURED, not chosen by tag: `mcr.microsoft.com/dotnet/sdk:8.0` carries `git` and
28585
+ * `dotnet` (881MB). `git` is the property the whole `image-tooling-live` test exists
28586
+ * for, and `eclipse-temurin:21-jdk` was rejected here for lacking it.
28587
+ *
28588
+ * The tag shape fits without touching the builder: the SDK publishes a tag per
28589
+ * version, so `repository:VERSION` is a real image at every version envspec can
28590
+ * derive, with no suffix. That is NOT true of the maven images, where the leading
28591
+ * tag component is maven's version and Java's sits in the suffix.
28592
+ *
28593
+ * `declaredIsFloor` is true on the same reasoning as go and rust: an SDK builds
28594
+ * older target frameworks, so booting 8.0 for a repo asking 6.0 is the supported
28595
+ * case rather than a substitution. A repo asking for something NEWER than our
28596
+ * default still refuses, which is where TERM-643 put that line.
28597
+ */
28598
+ dotnet: {
28599
+ repository: "mcr.microsoft.com/dotnet/sdk",
28600
+ suffix: "",
28601
+ defaultVersion: "8.0",
28602
+ declaredIsFloor: true
28603
+ },
28604
+ /**
28605
+ * TERM-1122. Maven's image, and the one a Gradle WRAPPER repo gets too: the wrapper
28606
+ * downloads its own Gradle, so it needs a JDK and nothing else. MEASURED
28607
+ * 2026-09-18: `maven:3.9-eclipse-temurin-21` carries `git`, `mvn` and JDK 21 on
28608
+ * Ubuntu 24.04, which is why this is not `eclipse-temurin` (no `git`, rejected
28609
+ * under TERM-1139). The JDK sits AFTER maven's own version in the tag, hence
28610
+ * `prefix`, and the major is all envspec derives (`javaMajor`).
28611
+ *
28612
+ * `declaredIsFloor`: a JDK builds for older `--release` targets, so a repo asking
28613
+ * for 16, which has no image, runs on 21. A Gradle toolchain is stricter than a
28614
+ * floor and asks for its exact JDK; Gradle's refusal on the wrong one reads as
28615
+ * ours (`classify.ts`, `isOfflineBuildGap`), never as the repo's.
28616
+ */
28617
+ jvm: {
28618
+ repository: "maven",
28619
+ prefix: "3.9-eclipse-temurin-",
28620
+ suffix: "",
28621
+ defaultVersion: "21",
28622
+ declaredIsFloor: true
28623
+ }
28624
+ };
28625
+ JVM_GRADLE_IMAGE = {
28626
+ repository: "gradle",
28627
+ prefix: "jdk",
28628
+ suffix: "",
28629
+ defaultVersion: "21",
28630
+ declaredIsFloor: true
28631
+ };
28632
+ unversionedImage = (shape) => `${shape.repository}:${shape.prefix ?? ""}${shape.defaultVersion}${shape.suffix}`;
28297
28633
  TAG_VERSION = /^\d+(?:\.\d+){0,2}$/;
28298
28634
  dockerManifestProbe = (image) => {
28299
28635
  const res = spawnSync4("docker", ["manifest", "inspect", image], {
@@ -28985,14 +29321,31 @@ function answerDidItPass(r) {
28985
29321
  const passed = r.status === "verified" && r.outcome !== null && isGreen(r.outcome);
28986
29322
  return { passed, summary: renderVerdictLine(r), lookAt: r.preview?.url ?? null };
28987
29323
  }
28988
- var RUN_TEST_COMMAND_SOURCES, RUN_RESULT_SCHEMA, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
29324
+ function exitCodeFor(r) {
29325
+ if (r.status === "refused")
29326
+ return 2;
29327
+ if (r.outcome === null)
29328
+ return 2;
29329
+ return OUTCOME_EXIT_CODES[r.outcome];
29330
+ }
29331
+ var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS, OUTCOME_EXIT_CODES;
28989
29332
  var init_result = __esm({
28990
29333
  "../../packages/envrun/dist/result.js"() {
28991
29334
  "use strict";
28992
29335
  init_dist2();
28993
29336
  init_classify2();
28994
29337
  init_venueDescriptor();
28995
- RUN_TEST_COMMAND_SOURCES = [...TEST_COMMAND_SOURCES, "developer-declared"];
29338
+ RUN_TEST_COMMAND_SOURCES = [
29339
+ ...TEST_COMMAND_SOURCES,
29340
+ "developer-declared",
29341
+ "operator-declared"
29342
+ ];
29343
+ RUN_IMAGE_SOURCES = [
29344
+ "detected",
29345
+ "none",
29346
+ "operator-declared",
29347
+ "developer-declared"
29348
+ ];
28996
29349
  RUN_RESULT_SCHEMA = "terminalhire.verification-run/1";
28997
29350
  RUN_RESULT_FIELDS = [
28998
29351
  "schema",
@@ -29017,7 +29370,9 @@ var init_result = __esm({
29017
29370
  "preview",
29018
29371
  "containerImage",
29019
29372
  "containerImageDigest",
29373
+ "imageSource",
29020
29374
  "leaksClean",
29375
+ "leakState",
29021
29376
  "venue"
29022
29377
  ];
29023
29378
  RENDER_NONE = null;
@@ -29049,12 +29404,42 @@ var init_result = __esm({
29049
29404
  preview: (r) => r.preview === null ? null : `preview ${r.preview.url}`,
29050
29405
  containerImage: (r) => r.containerImage === null ? null : `image ${r.containerImage}`,
29051
29406
  containerImageDigest: (r) => r.containerImageDigest === null ? null : `image digest ${r.containerImageDigest}`,
29052
- leaksClean: (r) => r.leaksClean === null ? null : r.leaksClean ? null : "WARNING labelled Docker objects survived teardown",
29407
+ // Shown only when a human chose the environment. `detected` is the ordinary case and
29408
+ // saying so on every run would train the reader to skip the line that matters.
29409
+ imageSource: (r) => r.imageSource === "detected" || r.imageSource === "none" ? null : `image source ${r.imageSource} (not signed)`,
29410
+ // Printed through `leakState` below, which knows WHY a false is false. Rendering both would
29411
+ // print the leak warning on a run where we merely could not look (TERM-1144).
29412
+ leaksClean: RENDER_NONE,
29413
+ // Silent when clean and when refused, as the boolean's line was. The two not-a-leak states
29414
+ // get their own sentence, because "we could not check" read as "we found a leak" is the
29415
+ // exact confusion this field exists to end.
29416
+ leakState: (r) => {
29417
+ switch (r.leakState) {
29418
+ case null:
29419
+ case "clean":
29420
+ return null;
29421
+ case "leak":
29422
+ return "WARNING labelled Docker objects survived teardown";
29423
+ case "inconclusive":
29424
+ return "leak check inconclusive \u2014 no labelled container was seen while the run was live, so finding nothing afterwards proves nothing";
29425
+ case "unobserved":
29426
+ return "leak check not taken \u2014 we could not check what was left after teardown";
29427
+ }
29428
+ },
29053
29429
  // Absent on most runs, so it prints only when there is something to say. Silence
29054
29430
  // here is the honest rendering of "no venue answered": a placeholder line would
29055
29431
  // invite a reader to treat an unanswered probe as a described venue.
29056
29432
  venue: (r) => r.venue === null ? null : renderVenueLine(r.venue)
29057
29433
  };
29434
+ OUTCOME_EXIT_CODES = {
29435
+ completed: 0,
29436
+ "tests-failed": 1,
29437
+ "no-tests-observed": 1,
29438
+ "budget-exceeded": 1,
29439
+ "counts-unparsed": 2,
29440
+ "test-command-unavailable": 2,
29441
+ "environment-exhausted": 2
29442
+ };
29058
29443
  }
29059
29444
  });
29060
29445
 
@@ -29175,12 +29560,23 @@ function toAcceptancePredicate(pair, opts = {}) {
29175
29560
  if (why !== null)
29176
29561
  return refuse3("test-command-origin-unattested", `the ${side} run ${why}`);
29177
29562
  }
29178
- if (patched.testCommandSource === "developer-declared") {
29179
- return refuse3("test-command-origin-unattested", "the developer supplied the test command (unreachable \u2014 the source rules above cover it)");
29563
+ if (patched.testCommandSource === "developer-declared" || patched.testCommandSource === "operator-declared") {
29564
+ return refuse3("test-command-origin-unattested", `${patched.testCommandSource === "developer-declared" ? "the developer" : "an operator"} supplied the test command (unreachable \u2014 the source rules above cover it)`);
29180
29565
  }
29181
29566
  if (baseline.testCommandSource !== patched.testCommandSource) {
29182
29567
  return refuse3("test-command-origin-unattested", `the pair disagrees on where the command came from: baseline ${baseline.testCommandSource}, patched ${patched.testCommandSource}. One signed value cannot describe both.`);
29183
29568
  }
29569
+ for (const [side, source, image] of [
29570
+ ["patched", patched.imageSource, patched.containerImage],
29571
+ ["baseline", baseline.imageSource, baseline.containerImage]
29572
+ ]) {
29573
+ const why = IMAGE_SOURCE_IS_SIGNABLE[source](image);
29574
+ if (why !== null)
29575
+ return refuse3("image-origin-unattested", `the ${side} run ${why}`);
29576
+ }
29577
+ if (baseline.imageSource !== patched.imageSource) {
29578
+ return refuse3("image-origin-unattested", `the pair disagrees on where the image came from: baseline ${baseline.imageSource}, patched ${patched.imageSource}. The environment is part of what the statement says was held constant.`);
29579
+ }
29184
29580
  if (baseline.containerImage !== patched.containerImage) {
29185
29581
  return refuse3("pair-disagrees-on-image", `baseline ran in ${String(baseline.containerImage)} and patched in ${String(patched.containerImage)}. The signed measurement names one image, so a pair from two environments would attribute to the patch whatever the image changed.`);
29186
29582
  }
@@ -29237,7 +29633,7 @@ function signRunStatement(predicate, privateKey, keyid) {
29237
29633
  const statement = createAcceptanceStatement(predicate);
29238
29634
  return { statement, envelope: signStatement(statement, privateKey, keyid) };
29239
29635
  }
29240
- var OUTCOME_TO_BUDGET, BASELINE_IS_A_VERDICT, CONTRADICTS_COUNTS, SOURCE_IS_SIGNABLE, ATTEST_REFUSAL_REASONS, refuse3, LOCAL_MEASUREMENT_PREFIX, REFERENCE_DOMAIN_COMPONENT, REFERENCE_DOMAIN_NAME, REFERENCE_IPV6, REFERENCE_DOMAIN, REFERENCE_PATH_COMPONENT, REPO_DIGEST_RE;
29636
+ var OUTCOME_TO_BUDGET, BASELINE_IS_A_VERDICT, CONTRADICTS_COUNTS, SOURCE_IS_SIGNABLE, IMAGE_SOURCE_IS_SIGNABLE, ATTEST_REFUSAL_REASONS, refuse3, LOCAL_MEASUREMENT_PREFIX, REFERENCE_DOMAIN_COMPONENT, REFERENCE_DOMAIN_NAME, REFERENCE_IPV6, REFERENCE_DOMAIN, REFERENCE_PATH_COMPONENT, REPO_DIGEST_RE;
29241
29637
  var init_attestation2 = __esm({
29242
29638
  "../../packages/envrun/dist/attestation.js"() {
29243
29639
  "use strict";
@@ -29293,11 +29689,25 @@ var init_attestation2 = __esm({
29293
29689
  // The party under verification chose it, and no signed member can say so. Per ADR-005
29294
29690
  // Decision 2, emit nothing rather than pick the closest word.
29295
29691
  "developer-declared": () => "had its test command supplied by the developer whose work is under verification, and the signed vocabulary has no member for that. `founder-declared` would attribute the choice to the other party; `detected` would claim the repo chose it. Both are false.",
29692
+ // US, out of band, per posting — the twin of `IMAGE_SOURCE_IS_SIGNABLE['operator-declared']`
29693
+ // and refusing for the same reason. `founder-declared` is the nearest signed member and it
29694
+ // is still false: the poster did not choose this, we did, and signing our own choice under
29695
+ // the counterparty's name is the specific false attribution the `developer-declared` entry
29696
+ // above exists to refuse. Decision 2 — emit nothing rather than pick the closest word.
29697
+ "operator-declared": () => "had its test command chosen by an operator out of band, and the signed vocabulary has no member for that. Signing it would present a hand-picked command as one the repository chose for itself.",
29296
29698
  // `none` means nobody chose a command, so a command beside it is a contradiction. Shadowed
29297
29699
  // today — `thrun.ts` sets `none` only when the derived command is null, and `no-test-command`
29298
29700
  // fires first — but that coupling runs through `runEnvironmentSpec` and lives in no type.
29299
29701
  none: (command) => command === null ? null : `records its command source as \`none\` \u2014 nobody chose one \u2014 beside the command ${JSON.stringify(command)}`
29300
29702
  };
29703
+ IMAGE_SOURCE_IS_SIGNABLE = {
29704
+ detected: () => null,
29705
+ "operator-declared": () => "had its container image chosen by an operator out of band, and the signed vocabulary has no member for that. Signing it would present a hand-picked environment as the one the runtime mapping selected.",
29706
+ "developer-declared": () => "had its container image supplied by the developer whose work is under verification. A chosen interpreter turns a red suite green as surely as a chosen test command.",
29707
+ // `none` means no image was booted, so an image name beside it is a contradiction — the
29708
+ // `SOURCE_IS_SIGNABLE.none` argument, applied to the other field.
29709
+ none: (image) => image === null ? null : `records its image source as \`none\` \u2014 nothing was booted \u2014 beside the image ${JSON.stringify(image)}`
29710
+ };
29301
29711
  ATTEST_REFUSAL_REASONS = [
29302
29712
  "our-environment-failed",
29303
29713
  "run-was-refused",
@@ -29339,7 +29749,11 @@ var init_attestation2 = __esm({
29339
29749
  // The signed triple must be reproducible from what it names — see the guard.
29340
29750
  "patch-binding-incomplete",
29341
29751
  "outcome-contradicts-counts",
29342
- "test-command-origin-unattested"
29752
+ "test-command-origin-unattested",
29753
+ // The environment half of the pair above. `pair-disagrees-on-image` compares image NAMES;
29754
+ // this one asks who CHOSE the name, which no comparison of the two halves can reveal when
29755
+ // both were handed the same override.
29756
+ "image-origin-unattested"
29343
29757
  ];
29344
29758
  refuse3 = (reason, detail) => ({
29345
29759
  ok: false,
@@ -29843,13 +30257,20 @@ function gcpBootArgv(p) {
29843
30257
  // steal". Attaching an account removes that, so the safety now rests on two things,
29844
30258
  // and this change MUST land together with proof #2 — never ahead of it:
29845
30259
  //
29846
- // 1. `--no-scopes` + ZERO project roles bound the BLAST RADIUS of a lifted token.
29847
- // No scopes → the OAuth access-token endpoint serves nothing (measured,
29848
- // TERM-706); zero roles → the token authorizes no GCP API. But it is NOT
29849
- // valueless: it is the attestation evidence itself, so a lifted token still
29850
- // authorizes a false claim about WHERE a run happened (the steal-a-true-token
29851
- // attack in the item-2 spike). `--no-scopes` stays because it removes lateral
29852
- // GCP access; it does not make losing the identity token cheap.
30260
+ // 1. `--no-scopes` and a NARROW grant set bound the BLAST RADIUS of a lifted
30261
+ // token. No scopes → the OAuth access-token endpoint serves nothing
30262
+ // (measured, TERM-706). This said "ZERO project roles" until 2026-09-18, and
30263
+ // that was never true of the account we ship: measured live, `venue-identity@`
30264
+ // holds `roles/confidentialcomputing.workloadUser` on the project and
30265
+ // `roles/artifactregistry.reader` on the `venue-images` repository alone —
30266
+ // the same two grants the Confidential Space note below already named, so this
30267
+ // file contradicted itself. Neither reaches a second repository, a bucket or
30268
+ // another VM, which is the property "zero roles" was reaching for. But the
30269
+ // token is NOT valueless: it is the attestation evidence itself, so a lifted
30270
+ // token still authorizes a false claim about WHERE a run happened (the
30271
+ // steal-a-true-token attack in the item-2 spike). `--no-scopes` stays because
30272
+ // it removes lateral GCP access; it does not make losing the identity token
30273
+ // cheap.
29853
30274
  // 2. The untrusted code cannot reach the metadata server to lift the token at all.
29854
30275
  // The test step runs `--network=none`; the install step runs on an `--internal`
29855
30276
  // network whose only egress is a CONNECT-only, registry-allowlisted proxy that
@@ -31329,11 +31750,7 @@ function makeLease(p) {
31329
31750
  }
31330
31751
  try {
31331
31752
  check("counting what the run left behind");
31332
- return Promise.resolve({
31333
- observed: true,
31334
- census: census(p.docker, label),
31335
- unobservedReason: null
31336
- });
31753
+ return Promise.resolve(censusReport(p.docker, label));
31337
31754
  } catch (err) {
31338
31755
  return Promise.resolve({
31339
31756
  observed: false,
@@ -31601,7 +32018,7 @@ function localDockerPlacement() {
31601
32018
  kind: "local-docker",
31602
32019
  refusal: null,
31603
32020
  venue: () => localVenue(),
31604
- imageFor: (runtime, override, version) => imageForRuntime(runtime, override, version)
32021
+ imageFor: (runtime, override, version, variant) => imageForRuntime(runtime, override, version, variant)
31605
32022
  };
31606
32023
  }
31607
32024
  function hostedPoolPlacement() {
@@ -31609,7 +32026,7 @@ function hostedPoolPlacement() {
31609
32026
  kind: "hosted-pool",
31610
32027
  refusal: null,
31611
32028
  venue: () => hostedVenue(),
31612
- imageFor: (runtime, override, version) => imageForRuntime(runtime, override, version)
32029
+ imageFor: (runtime, override, version, variant) => imageForRuntime(runtime, override, version, variant)
31613
32030
  };
31614
32031
  }
31615
32032
  function placementFor(kind) {
@@ -32174,6 +32591,9 @@ function detectRuntime(repo) {
32174
32591
  if (repo.listNames("").some((name) => /\.(?:csproj|fsproj|sln)$/.test(name))) {
32175
32592
  candidates.push("dotnet");
32176
32593
  }
32594
+ if (candidates.length === 0 && repo.listFiles("").some((f) => f.endsWith(".java"))) {
32595
+ candidates.push("jvm");
32596
+ }
32177
32597
  return { runtime: candidates[0] ?? "unknown", candidates };
32178
32598
  }
32179
32599
  function readJsonObject(repo, path5) {
@@ -32321,6 +32741,11 @@ function basePythonInstallCommand(repo) {
32321
32741
  }
32322
32742
  return null;
32323
32743
  }
32744
+ function rehearse(command) {
32745
+ const copy = '"$R/.terminalhire-rehearsal"';
32746
+ const home = '"$R/.terminalhire-rehearsal-home"';
32747
+ return `{ R="$HOME"; rm -rf ${copy} ${home}; mkdir -p ${home} && cp -R . ${copy} && (cd ${copy} && HOME=${home} GRADLE_USER_HOME="$R/.gradle" JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -Duser.home=$R/.terminalhire-rehearsal-home" ${command}); rm -rf ${copy} ${home}; true; }`;
32748
+ }
32324
32749
  function otherRuntimeCommands(repo, runtime) {
32325
32750
  switch (runtime) {
32326
32751
  case "go":
@@ -32348,10 +32773,21 @@ function otherRuntimeCommands(repo, runtime) {
32348
32773
  case "dotnet":
32349
32774
  return { install: "dotnet restore", test: "dotnet test" };
32350
32775
  case "jvm": {
32351
- if (repo.exists("pom.xml"))
32352
- return { install: "mvn -B dependency:go-offline", test: "mvn -B test" };
32776
+ if (!JVM_BUILD_FILES.some((file) => repo.exists(file)))
32777
+ return { install: null, test: null };
32778
+ if (repo.exists("pom.xml")) {
32779
+ return {
32780
+ install: `mvn -B dependency:go-offline && ${rehearse('mvn -B test -Dmaven.test.failure.ignore=true -Dmaven.repo.local="$R/.m2/repository"')}`,
32781
+ test: "mvn -B -o test"
32782
+ };
32783
+ }
32353
32784
  const wrapper = repo.exists("gradlew") ? "./gradlew" : "gradle";
32354
- return { install: `${wrapper} dependencies`, test: `${wrapper} test` };
32785
+ return {
32786
+ install: `${wrapper} dependencies && ${rehearse(`${wrapper} test --continue`)}`,
32787
+ // `--no-build-cache`: the rehearsal above can fill Gradle's build cache, and a
32788
+ // test task restored FROM-CACHE runs nothing and prints no count.
32789
+ test: `${wrapper} test --offline --no-build-cache`
32790
+ };
32355
32791
  }
32356
32792
  default:
32357
32793
  return { install: null, test: null };
@@ -32383,6 +32819,15 @@ function searchRuntimeVersion(repo, runtime) {
32383
32819
  push(".ruby-version", repo.readText(".ruby-version"));
32384
32820
  } else if (runtime === "dotnet") {
32385
32821
  push("global.json sdk.version", readStringField(readObjectField(readJsonObject(repo, "global.json"), "sdk"), "version"));
32822
+ } else if (runtime === "jvm") {
32823
+ const pom = repo.readText("pom.xml");
32824
+ const gradle = repo.readText("build.gradle") ?? repo.readText("build.gradle.kts");
32825
+ push("build.gradle toolchain languageVersion", javaMajor(matchFirst(gradle, /JavaLanguageVersion\.of\(\s*["']?(\d+)["']?\s*\)/) ?? matchFirst(gradle, /jvmToolchain\(\s*(\d+)\s*\)/)));
32826
+ push(".java-version", javaMajor(repo.readText(".java-version")));
32827
+ push("pom.xml maven.compiler.release", javaMajor(matchFirst(pom, /<maven\.compiler\.release>\s*([^<\s]+)\s*</)));
32828
+ push("pom.xml java.version", javaMajor(matchFirst(pom, /<java\.version>\s*([^<\s]+)\s*</)));
32829
+ push("pom.xml maven.compiler.source", javaMajor(matchFirst(pom, /<maven\.compiler\.source>\s*([^<\s]+)\s*</)));
32830
+ push("build.gradle sourceCompatibility", javaMajor(matchFirst(gradle, /sourceCompatibility\s*=\s*JavaVersion\.VERSION_([\d_]+)/)?.replace("_", ".") ?? matchFirst(gradle, /sourceCompatibility\s*=\s*["']?([\d.]+)["']?/)));
32386
32831
  }
32387
32832
  let rangeFound = null;
32388
32833
  for (const source of sources) {
@@ -32396,13 +32841,21 @@ function searchRuntimeVersion(repo, runtime) {
32396
32841
  }
32397
32842
  return { version: null, searched: sources.map((s) => s.label), rangeFound };
32398
32843
  }
32844
+ function javaMajor(text) {
32845
+ if (text === null)
32846
+ return null;
32847
+ const match2 = /^(?:1\.(\d+)|(\d+))(?:[._][\d_.]*)?$/.exec(text.trim());
32848
+ if (match2 === null)
32849
+ return null;
32850
+ return match2[1] ?? match2[2] ?? null;
32851
+ }
32399
32852
  function matchFirst(text, pattern) {
32400
32853
  if (text === null)
32401
32854
  return null;
32402
32855
  const match2 = pattern.exec(text);
32403
32856
  return match2 === null ? null : match2[1];
32404
32857
  }
32405
- var RUNTIME_MANIFESTS, MANIFEST_FILENAMES, NPM_PLACEHOLDER_TEST, REQUIREMENT_SPECIFIER, EXACT_VERSION;
32858
+ var RUNTIME_MANIFESTS, MANIFEST_FILENAMES, JVM_BUILD_FILES, NPM_PLACEHOLDER_TEST, REQUIREMENT_SPECIFIER, EXACT_VERSION;
32406
32859
  var init_manifest2 = __esm({
32407
32860
  "../../packages/envspec/dist/manifest.js"() {
32408
32861
  "use strict";
@@ -32416,6 +32869,7 @@ var init_manifest2 = __esm({
32416
32869
  { runtime: "dotnet", files: [] }
32417
32870
  ];
32418
32871
  MANIFEST_FILENAMES = RUNTIME_MANIFESTS.flatMap((m) => m.files).concat(["*.csproj", "*.fsproj", "*.sln"]).sort();
32872
+ JVM_BUILD_FILES = ["build.gradle", "build.gradle.kts", "pom.xml"];
32419
32873
  NPM_PLACEHOLDER_TEST = /^echo\s+["']?Error:\s*no test specified["']?\s*&&\s*exit\s+1$/;
32420
32874
  REQUIREMENT_SPECIFIER = /^[A-Za-z0-9._-]+(\[[A-Za-z0-9._,-]+\])?([<>=!~]=?[A-Za-z0-9._*+-]+(,[<>=!~]=?[A-Za-z0-9._*+-]+)*)?$/;
32421
32875
  EXACT_VERSION = /^v?(\d+(?:\.\d+){0,2})$/;
@@ -32812,7 +33266,7 @@ function deriveFromReader(repo) {
32812
33266
  searched: MANIFEST_FILENAMES,
32813
33267
  detail: `no ecosystem manifest was found at the repo root (looked for ${MANIFEST_FILENAMES.join(", ")}), so the language runtime could not be identified`
32814
33268
  });
32815
- } else {
33269
+ } else if (runtime !== "jvm" || JVM_BUILD_FILES.some((file) => repo.exists(file))) {
32816
33270
  derivedFrom.add("manifest");
32817
33271
  }
32818
33272
  if (detection.candidates.length > 1) {
@@ -32976,12 +33430,13 @@ function deriveRuntimeVersion(repo, runtime, job) {
32976
33430
  const setup = SETUP_ACTIONS[runtime];
32977
33431
  const searched = [];
32978
33432
  let rangeFound = null;
33433
+ const pin = (raw) => runtime === "jvm" ? javaMajor(raw) : exactVersion(raw);
32979
33434
  if (job !== null && setup !== void 0) {
32980
33435
  const direct = findActionInput(job, setup.action, setup.input);
32981
33436
  if (direct !== null) {
32982
33437
  searched.push(`${job.file} ${setup.action} ${setup.input}`);
32983
33438
  const reduced = reduceExpression(direct.command, job.singletons);
32984
- const exact = exactVersion(reduced);
33439
+ const exact = pin(reduced);
32985
33440
  if (exact !== null)
32986
33441
  return { value: exact, searched, rangeFound: null };
32987
33442
  if (reduced !== null)
@@ -32990,7 +33445,7 @@ function deriveRuntimeVersion(repo, runtime, job) {
32990
33445
  const viaFile = findActionInput(job, setup.action, setup.fileInput);
32991
33446
  if (viaFile !== null) {
32992
33447
  searched.push(`${job.file} ${setup.action} ${setup.fileInput} -> ${viaFile.command}`);
32993
- const exact = exactVersion(repo.readText(viaFile.command));
33448
+ const exact = pin(repo.readText(viaFile.command));
32994
33449
  if (exact !== null)
32995
33450
  return { value: exact, searched, rangeFound: null };
32996
33451
  }
@@ -33015,6 +33470,8 @@ function testSearchPaths(runtime, workflowFiles) {
33015
33470
  paths.push("**/*_test.go");
33016
33471
  if (runtime === "ruby")
33017
33472
  paths.push("Gemfile", "Rakefile");
33473
+ if (runtime === "jvm")
33474
+ paths.push(...JVM_BUILD_FILES);
33018
33475
  if (runtime === "unknown")
33019
33476
  paths.push(...MANIFEST_FILENAMES);
33020
33477
  return [...new Set(paths)].sort();
@@ -33032,6 +33489,9 @@ function describeTestSearch(repo, runtime, workflowFiles) {
33032
33489
  if (runtime === "python") {
33033
33490
  return `${ciPart}, and nothing in the repo names a test runner \u2014 no \`[tool.pytest.ini_options]\`, no \`pytest.ini\`, and no pytest dependency in \`pyproject.toml\` or a requirements file`;
33034
33491
  }
33492
+ if (runtime === "jvm" && !JVM_BUILD_FILES.some((file) => repo.exists(file))) {
33493
+ return `${ciPart}, and the repo has Java sources but no build file (${JVM_BUILD_FILES.join(", ")}), so there is no build to run tests with. Compiling the sources with \`javac\` alone would run no tests`;
33494
+ }
33035
33495
  return `${ciPart}, and no ${runtime} test convention applies to this tree`;
33036
33496
  }
33037
33497
  function installSearchPaths(runtime, workflowFiles) {
@@ -33040,6 +33500,8 @@ function installSearchPaths(runtime, workflowFiles) {
33040
33500
  paths.push("package-lock.json", "pnpm-lock.yaml", "yarn.lock");
33041
33501
  if (runtime === "python")
33042
33502
  paths.push("poetry.lock", "pyproject.toml", "requirements.txt", "uv.lock");
33503
+ if (runtime === "jvm")
33504
+ paths.push(...JVM_BUILD_FILES);
33043
33505
  if (runtime === "unknown")
33044
33506
  paths.push(...MANIFEST_FILENAMES);
33045
33507
  return [...new Set(paths)].sort();
@@ -33118,12 +33580,41 @@ var init_dist3 = __esm({
33118
33580
  }
33119
33581
  });
33120
33582
 
33583
+ // ../../packages/envrun/dist/gradleTestSummary.js
33584
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync14 } from "fs";
33585
+ import { join as join26 } from "path";
33586
+ function stageGradleTestSummary(jailHome) {
33587
+ const dir = join26(jailHome, ".gradle", "init.d");
33588
+ mkdirSync5(dir, { recursive: true });
33589
+ writeFileSync14(join26(dir, "terminalhire-test-summary.gradle"), GRADLE_TEST_SUMMARY_INIT);
33590
+ }
33591
+ var GRADLE_TEST_MARKER, GRADLE_TEST_SUMMARY_INIT;
33592
+ var init_gradleTestSummary = __esm({
33593
+ "../../packages/envrun/dist/gradleTestSummary.js"() {
33594
+ "use strict";
33595
+ GRADLE_TEST_MARKER = "terminalhire-gradle-tests:";
33596
+ GRADLE_TEST_SUMMARY_INIT = `// Written by terminalhire (TERM-1122): prints each Test task's counts, because
33597
+ // Gradle prints none when every test passes.
33598
+ allprojects {
33599
+ tasks.withType(Test).configureEach { task ->
33600
+ def label = task.path
33601
+ task.afterSuite { desc, result ->
33602
+ if (desc.parent == null) {
33603
+ println "${GRADLE_TEST_MARKER} task=\${label} total=\${result.testCount} passed=\${result.successfulTestCount} failed=\${result.failedTestCount} skipped=\${result.skippedTestCount}"
33604
+ }
33605
+ }
33606
+ }
33607
+ }
33608
+ `;
33609
+ }
33610
+ });
33611
+
33121
33612
  // ../../packages/envrun/dist/thrun.js
33122
33613
  import { execFileSync, spawnSync as spawnSync6 } from "child_process";
33123
- import { existsSync as existsSync12, mkdirSync as mkdirSync5, mkdtempSync as mkdtempSync4, rmSync as rmSync9 } from "fs";
33614
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync4, rmSync as rmSync9 } from "fs";
33124
33615
  import { randomUUID as randomUUID3 } from "crypto";
33125
33616
  import { tmpdir as tmpdir4 } from "os";
33126
- import { join as join26 } from "path";
33617
+ import { join as join27 } from "path";
33127
33618
  function git(repoDir, args, allowNonZero = false) {
33128
33619
  const res = spawnSync6("git", [...args], {
33129
33620
  cwd: repoDir,
@@ -33138,7 +33629,7 @@ function git(repoDir, args, allowNonZero = false) {
33138
33629
  return res.stdout ?? "";
33139
33630
  }
33140
33631
  function collectWorkingDiff(repoDir, opts = {}) {
33141
- if (!existsSync12(join26(repoDir, ".git"))) {
33632
+ if (!existsSync12(join27(repoDir, ".git"))) {
33142
33633
  throw new ThRunError(`${repoDir} is not a git checkout (no .git). \`th run\` ships the working diff, so it needs a repository to read one from.`);
33143
33634
  }
33144
33635
  const headSha = git(repoDir, ["rev-parse", "HEAD"]).trim();
@@ -33286,7 +33777,7 @@ function credentialFreeHome() {
33286
33777
  return credentialFreeHomeDir;
33287
33778
  let made;
33288
33779
  try {
33289
- made = mkdtempSync4(join26(tmpdir4(), "th-run-nohome-"));
33780
+ made = mkdtempSync4(join27(tmpdir4(), "th-run-nohome-"));
33290
33781
  } catch (err) {
33291
33782
  throw new RunRefusalError("could not create the empty directory this clone uses as its home, so the clone would read the credentials on this machine instead. That is our environment failing, not your tests: check that the temp directory is writable.", { cause: err });
33292
33783
  }
@@ -33361,7 +33852,7 @@ function cloneTargetAtUnguarded(opts) {
33361
33852
  if (persisted !== null) {
33362
33853
  throw new RunRefusalError(`refusing to clone from a URL carrying ${persisted}: \`git remote add\` writes the source verbatim into .git/config, which is mounted where the repo\u2019s own test command runs. Fetch with the credential out of band so it is never written to disk \u2014 this runner takes one as an HTTP header, which is never persisted.`);
33363
33854
  }
33364
- mkdirSync5(opts.dest, { recursive: true });
33855
+ mkdirSync6(opts.dest, { recursive: true });
33365
33856
  const runOut = (args) => execFileSync("git", [...gitConfigArgs(), ...args], {
33366
33857
  cwd: opts.dest,
33367
33858
  encoding: "utf8",
@@ -33400,7 +33891,7 @@ function cloneTargetAtUnguarded(opts) {
33400
33891
  }
33401
33892
  function scrubCloneSource(dest, run2) {
33402
33893
  run2(["remote", "remove", "origin"]);
33403
- rmSync9(join26(dest, ".git", "FETCH_HEAD"), { force: true });
33894
+ rmSync9(join27(dest, ".git", "FETCH_HEAD"), { force: true });
33404
33895
  }
33405
33896
  function publishableTarget(url) {
33406
33897
  if (separatorInTarget(url) !== null)
@@ -33539,12 +34030,18 @@ function refusedRun(fields) {
33539
34030
  treeDigest: null,
33540
34031
  baselinePatchSha256: null,
33541
34032
  testCommandSource: "none",
34033
+ // Nothing was booted, so nobody chose an image. Same reasoning as `containerImage: null`
34034
+ // below: it must not read as an image we picked and cannot name.
34035
+ imageSource: "none",
33542
34036
  boundaryRefusals: fields.boundaryRefusals,
33543
34037
  touchedPaths: fields.touchedPaths,
33544
34038
  preview: null,
33545
34039
  containerImage: null,
33546
34040
  containerImageDigest: null,
33547
34041
  leaksClean: null,
34042
+ // Null for the same reason, and never `unobserved`: that word says we tried to take a
34043
+ // census and could not, and here there was nothing to take one of (TERM-1144).
34044
+ leakState: null,
33548
34045
  // A refused run never held a lease, so there is no venue to describe. Same
33549
34046
  // reasoning as `leaksClean` above: null because nothing happened, and it must
33550
34047
  // not read as a venue we looked at and could not name.
@@ -33772,10 +34269,10 @@ async function runVerification(req, ctx) {
33772
34269
  venueIdentity: null
33773
34270
  };
33774
34271
  }
33775
- const stage = join26(req.scratchRoot, runId);
33776
- const cloneDir = join26(stage, "clone");
33777
- const scratch = join26(stage, "scratch");
33778
- mkdirSync5(scratch, { recursive: true });
34272
+ const stage = join27(req.scratchRoot, runId);
34273
+ const cloneDir = join27(stage, "clone");
34274
+ const scratch = join27(stage, "scratch");
34275
+ mkdirSync6(scratch, { recursive: true });
33779
34276
  assertSafeTargetSha(req.targetSha);
33780
34277
  progress("clone", `${publishableTarget(req.targetRepo)} @ ${req.targetSha.slice(0, 12)}`);
33781
34278
  cloneTargetAt({
@@ -33802,7 +34299,8 @@ async function runVerification(req, ctx) {
33802
34299
  const derived = deriveEnvironmentSpec(cloneDir);
33803
34300
  const spec = req.testCommandOverride === void 0 ? derived : { ...derived, testCommand: req.testCommandOverride };
33804
34301
  progress("derive", `runtime=${spec.runtime} install=${String(spec.installCommand)} test=${String(spec.testCommand)}`);
33805
- const image = placement.imageFor(spec.runtime, req.image, spec.runtimeVersion);
34302
+ refuseUnbuildableSpec(spec);
34303
+ const image = placement.imageFor(spec.runtime, req.image, spec.runtimeVersion, imageVariantFor(spec));
33806
34304
  const resolved = await resolveLease(placement, runId);
33807
34305
  if (!resolved.ok) {
33808
34306
  return {
@@ -33837,10 +34335,12 @@ async function runVerification(req, ctx) {
33837
34335
  const lease = resolved.lease;
33838
34336
  try {
33839
34337
  buildJail(scratch);
34338
+ if (spec.runtime === "jvm")
34339
+ stageGradleTestSummary(localJailPaths(scratch).jail);
33840
34340
  const venuePaths = await lease.stage({
33841
34341
  cloneDir,
33842
34342
  scratchRoot: scratch,
33843
- previewDir: join26(stage, "preview"),
34343
+ previewDir: join27(stage, "preview"),
33844
34344
  // On a dispatched run the commit is the statement of what was tested, so
33845
34345
  // it rides with the tree and the venue seam refuses a tree that is not
33846
34346
  // that commit (design §6 item 4, TERM-892 — the guard lives in
@@ -33887,7 +34387,7 @@ async function runVerification(req, ctx) {
33887
34387
  // that `founder-declared` (as this did for one review round) signs the counterparty's
33888
34388
  // name onto the developer's choice, which is worse than laundering it as `detected`:
33889
34389
  // it is a specific false attribution inside a field a reviewer trusts.
33890
- testCommandSource: req.testCommandOverride !== void 0 ? req.testCommandOverrideOrigin === "founder" ? "founder-declared" : "developer-declared" : derived.testCommand === null ? "none" : "detected",
34390
+ testCommandSource: req.testCommandOverride !== void 0 ? req.testCommandOverrideOrigin === "founder" ? "founder-declared" : req.testCommandOverrideOrigin === "operator" ? "operator-declared" : "developer-declared" : derived.testCommand === null ? "none" : "detected",
33891
34391
  boundaryRefusals: [],
33892
34392
  touchedPaths: pre.touchedPaths,
33893
34393
  preview: null,
@@ -33897,7 +34397,14 @@ async function runVerification(req, ctx) {
33897
34397
  // that measures one; a run without it is recordable but not attestable —
33898
34398
  // `toAcceptancePredicate` refuses `missing-image-digest` (TERM-893).
33899
34399
  containerImageDigest: null,
34400
+ // The image half of the provenance pair above. `detected` means the runtime mapping
34401
+ // chose the environment; an override names the human. Defaulting an unstated origin to
34402
+ // `developer-declared` is deliberate and matches `testCommandSource`: `th run --image`
34403
+ // runs on the developer's machine, judging the developer's work, so attributing it to
34404
+ // the operator would sign our name onto their choice.
34405
+ imageSource: req.image === void 0 ? "detected" : req.imageOrigin === "operator" ? "operator-declared" : "developer-declared",
33900
34406
  leaksClean: verdict.leaks.clean,
34407
+ leakState: verdict.leaks.state,
33901
34408
  // Built from the LEASE, over the client that ran the steps — never from
33902
34409
  // `req.placement`, which is a request. `venueDescriptor.ts` carries the
33903
34410
  // reasoning and the #735 failure that makes the distinction load-bearing.
@@ -33955,6 +34462,7 @@ var init_thrun = __esm({
33955
34462
  init_execute();
33956
34463
  init_placement();
33957
34464
  init_venue();
34465
+ init_gradleTestSummary();
33958
34466
  init_venueDescriptor();
33959
34467
  init_result();
33960
34468
  ThRunError = class extends Error {
@@ -34198,7 +34706,7 @@ var init_dbplan = __esm({
34198
34706
  // ../../packages/envrun/dist/dbstack.js
34199
34707
  import { spawnSync as spawnSync7 } from "child_process";
34200
34708
  import { randomBytes as randomBytes11 } from "crypto";
34201
- import { mkdirSync as mkdirSync6 } from "fs";
34709
+ import { mkdirSync as mkdirSync7 } from "fs";
34202
34710
  function installCommandFor(runner) {
34203
34711
  switch (runner) {
34204
34712
  case "sql":
@@ -34429,7 +34937,7 @@ function startDatabase(req) {
34429
34937
  if (!ready.ok) {
34430
34938
  throw new DbStackError(`INFRASTRUCTURE/INCONCLUSIVE: Postgres never accepted queries at 127.0.0.1:${String(creds.port)} within the readiness window: ${ready.detail}`);
34431
34939
  }
34432
- const query = (sql) => {
34940
+ const query2 = (sql) => {
34433
34941
  const res = docker2([
34434
34942
  "exec",
34435
34943
  `--env=PGPASSWORD=${creds.password}`,
@@ -34477,7 +34985,7 @@ function startDatabase(req) {
34477
34985
  credentials: creds,
34478
34986
  image,
34479
34987
  readyMs: ready.ms,
34480
- query,
34988
+ query: query2,
34481
34989
  runOnNetwork,
34482
34990
  teardown
34483
34991
  };
@@ -34540,8 +35048,8 @@ async function installLocalMigrationTooling(opts) {
34540
35048
  };
34541
35049
  }
34542
35050
  const { jail, tmp } = buildJail(opts.scratchRoot);
34543
- mkdirSync6(jail, { recursive: true });
34544
- mkdirSync6(tmp, { recursive: true });
35051
+ mkdirSync7(jail, { recursive: true });
35052
+ mkdirSync7(tmp, { recursive: true });
34545
35053
  const spec = {
34546
35054
  profile: "install",
34547
35055
  clone: opts.repoDir,
@@ -34836,6 +35344,7 @@ __export(dist_exports, {
34836
35344
  GOOGLE_JWKS_URL: () => GOOGLE_JWKS_URL,
34837
35345
  HostedVenueError: () => HostedVenueError,
34838
35346
  JWKS_FETCH_TIMEOUT_MS: () => JWKS_FETCH_TIMEOUT_MS,
35347
+ LEAK_STATES: () => LEAK_STATES,
34839
35348
  LOCAL_MEASUREMENT_PREFIX: () => LOCAL_MEASUREMENT_PREFIX,
34840
35349
  LabelWatch: () => LabelWatch,
34841
35350
  MIN_GIT_VERSION_FOR_END_OF_OPTIONS: () => MIN_GIT_VERSION_FOR_END_OF_OPTIONS,
@@ -34852,6 +35361,7 @@ __export(dist_exports, {
34852
35361
  REDACTED_TARGET_SHA: () => REDACTED_TARGET_SHA,
34853
35362
  RELEASED_LEASE_CENSUS_REASON: () => RELEASED_LEASE_CENSUS_REASON,
34854
35363
  REPO_DIGEST_RE: () => REPO_DIGEST_RE,
35364
+ RUN_IMAGE_SOURCES: () => RUN_IMAGE_SOURCES,
34855
35365
  RUN_LABEL_KEY: () => RUN_LABEL_KEY,
34856
35366
  RUN_RESULT_FIELDS: () => RUN_RESULT_FIELDS,
34857
35367
  RUN_RESULT_SCHEMA: () => RUN_RESULT_SCHEMA,
@@ -34868,6 +35378,7 @@ __export(dist_exports, {
34868
35378
  VENUE_GCLOUD_CONFIG: () => VENUE_GCLOUD_CONFIG,
34869
35379
  VENUE_SSH_USER: () => VENUE_SSH_USER,
34870
35380
  VENV_DIR: () => VENV_DIR,
35381
+ VERIFICATION_OUTCOMES: () => VERIFICATION_OUTCOMES,
34871
35382
  VenueRollbackError: () => VenueRollbackError,
34872
35383
  acquireTransactionally: () => acquireTransactionally,
34873
35384
  alembicChainPosition: () => alembicChainPosition,
@@ -34881,6 +35392,7 @@ __export(dist_exports, {
34881
35392
  assertVenueOwnerDeclared: () => assertVenueOwnerDeclared,
34882
35393
  bookkeepingFor: () => bookkeepingFor,
34883
35394
  census: () => census,
35395
+ censusReport: () => censusReport,
34884
35396
  censusTotal: () => censusTotal,
34885
35397
  classifyBootFailure: () => classifyBootFailure,
34886
35398
  classifyProbeFailure: () => classifyProbeFailure,
@@ -34900,6 +35412,7 @@ __export(dist_exports, {
34900
35412
  detectRunner: () => detectRunner,
34901
35413
  endOfOptionsUnsupported: () => endOfOptionsUnsupported,
34902
35414
  ensureVenueServiceCredentials: () => ensureVenueServiceCredentials,
35415
+ exitCodeFor: () => exitCodeFor,
34903
35416
  failureSourceOf: () => failureSourceOf,
34904
35417
  findRunRefusal: () => findRunRefusal,
34905
35418
  gcpBootArgv: () => gcpBootArgv,
@@ -34914,6 +35427,7 @@ __export(dist_exports, {
34914
35427
  identityProbeCommand: () => identityProbeCommand,
34915
35428
  imageForRuntime: () => imageForRuntime,
34916
35429
  imageRepo: () => imageRepo,
35430
+ imageVariantFor: () => imageVariantFor,
34917
35431
  installCommandFor: () => installCommandFor,
34918
35432
  installLocalMigrationTooling: () => installLocalMigrationTooling,
34919
35433
  isBookkeepingTable: () => isBookkeepingTable,
@@ -34943,6 +35457,7 @@ __export(dist_exports, {
34943
35457
  readSchema: () => readSchema,
34944
35458
  recordedApplied: () => recordedApplied,
34945
35459
  refuseSshTransport: () => refuseSshTransport,
35460
+ refuseUnbuildableSpec: () => refuseUnbuildableSpec,
34946
35461
  renderRunReport: () => renderRunReport,
34947
35462
  renderVenueLine: () => renderVenueLine,
34948
35463
  renderVerdictLine: () => renderVerdictLine,
@@ -35029,8 +35544,8 @@ __export(cache_store_exports, {
35029
35544
  updateIndexCache: () => updateIndexCache,
35030
35545
  writeIndexCache: () => writeIndexCache
35031
35546
  });
35032
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync14, renameSync as renameSync5 } from "fs";
35033
- import { join as join27 } from "path";
35547
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync15, renameSync as renameSync5 } from "fs";
35548
+ import { join as join28 } from "path";
35034
35549
  import { homedir as homedir13 } from "os";
35035
35550
  function readCacheEntry() {
35036
35551
  try {
@@ -35049,7 +35564,7 @@ function updateIndexCache(patch) {
35049
35564
  ts: Date.now()
35050
35565
  };
35051
35566
  const tmp = `${INDEX_CACHE_FILE}.${process.pid}.${tmpCounter++}.tmp`;
35052
- writeFileSync14(tmp, JSON.stringify(entry), "utf8");
35567
+ writeFileSync15(tmp, JSON.stringify(entry), "utf8");
35053
35568
  renameSync5(tmp, INDEX_CACHE_FILE);
35054
35569
  return entry;
35055
35570
  }
@@ -35061,8 +35576,8 @@ var init_cache_store = __esm({
35061
35576
  "bin/cache-store.js"() {
35062
35577
  "use strict";
35063
35578
  init_state_dir();
35064
- TERMINALHIRE_DIR10 = process.env.TERMINALHIRE_DIR || join27(homedir13(), ".terminalhire");
35065
- INDEX_CACHE_FILE = join27(TERMINALHIRE_DIR10, "index-cache.json");
35579
+ TERMINALHIRE_DIR10 = process.env.TERMINALHIRE_DIR || join28(homedir13(), ".terminalhire");
35580
+ INDEX_CACHE_FILE = join28(TERMINALHIRE_DIR10, "index-cache.json");
35066
35581
  SCHEMA_VERSION2 = 1;
35067
35582
  tmpCounter = 0;
35068
35583
  }
@@ -35262,8 +35777,8 @@ var init_audit2 = __esm({
35262
35777
  init_src();
35263
35778
  import {
35264
35779
  readFileSync as readFileSync14,
35265
- writeFileSync as writeFileSync15,
35266
- mkdirSync as mkdirSync7,
35780
+ writeFileSync as writeFileSync16,
35781
+ mkdirSync as mkdirSync8,
35267
35782
  mkdtempSync as mkdtempSync5,
35268
35783
  renameSync as renameSync6,
35269
35784
  existsSync as existsSync13,
@@ -35272,7 +35787,7 @@ import {
35272
35787
  rmSync as rmSync10,
35273
35788
  readdirSync as readdirSync3
35274
35789
  } from "fs";
35275
- import { join as join28, dirname as dirname8, isAbsolute as isAbsolute4, resolve as pathResolve } from "path";
35790
+ import { join as join29, dirname as dirname8, isAbsolute as isAbsolute4, resolve as pathResolve } from "path";
35276
35791
  import { createHash as createHash10 } from "crypto";
35277
35792
  import { homedir as homedir14, hostname as osHostname } from "os";
35278
35793
  import { execFile as execFile3, execFileSync as execFileSync2, spawnSync as spawnSync8 } from "child_process";
@@ -35719,10 +36234,10 @@ async function syncFounderVerdicts({
35719
36234
 
35720
36235
  // bin/jpi-claim.js
35721
36236
  init_founder_note_sync();
35722
- var TERMINALHIRE_DIR11 = process.env.TERMINALHIRE_DIR || join28(homedir14(), ".terminalhire");
35723
- var INDEX_CACHE_FILE2 = join28(TERMINALHIRE_DIR11, "index-cache.json");
35724
- var CLAIM_PUSH_MARKER = join28(TERMINALHIRE_DIR11, "claim-push.json");
35725
- var REPO_CONTINUITY_NUDGE_MARKER = join28(TERMINALHIRE_DIR11, "repo-continuity-nudged.json");
36237
+ var TERMINALHIRE_DIR11 = process.env.TERMINALHIRE_DIR || join29(homedir14(), ".terminalhire");
36238
+ var INDEX_CACHE_FILE2 = join29(TERMINALHIRE_DIR11, "index-cache.json");
36239
+ var CLAIM_PUSH_MARKER = join29(TERMINALHIRE_DIR11, "claim-push.json");
36240
+ var REPO_CONTINUITY_NUDGE_MARKER = join29(TERMINALHIRE_DIR11, "repo-continuity-nudged.json");
35726
36241
  function readNudgedClaimIds() {
35727
36242
  try {
35728
36243
  const raw = JSON.parse(readFileSync14(REPO_CONTINUITY_NUDGE_MARKER, "utf8"));
@@ -35736,7 +36251,7 @@ function markClaimNudged(id) {
35736
36251
  const ids2 = readNudgedClaimIds();
35737
36252
  ids2.add(id);
35738
36253
  ensureStateDir(TERMINALHIRE_DIR11);
35739
- writeFileSync15(REPO_CONTINUITY_NUDGE_MARKER, JSON.stringify({ claimIds: [...ids2] }), "utf8");
36254
+ writeFileSync16(REPO_CONTINUITY_NUDGE_MARKER, JSON.stringify({ claimIds: [...ids2] }), "utf8");
35740
36255
  } catch {
35741
36256
  }
35742
36257
  }
@@ -38069,7 +38584,7 @@ async function cmdAttach(id, worktree, branch) {
38069
38584
  function workDirFor(repoFullName, issueNumber) {
38070
38585
  const [owner, repo] = String(repoFullName).split("/");
38071
38586
  const suffix = issueNumber ? `-${issueNumber}` : "";
38072
- return join28(homedir14(), "terminalhire", "work", `${owner}-${repo}${suffix}`);
38587
+ return join29(homedir14(), "terminalhire", "work", `${owner}-${repo}${suffix}`);
38073
38588
  }
38074
38589
  function startBranchFor(repoFullName, issueNumber) {
38075
38590
  const repo = String(repoFullName).split("/")[1] || "claim";
@@ -38325,7 +38840,7 @@ terminalhire claim: not started \u2014 starting forks ${claim.repoFullName} to y
38325
38840
  );
38326
38841
  process.exit(1);
38327
38842
  }
38328
- mkdirSync7(join28(homedir14(), "terminalhire", "work"), { recursive: true });
38843
+ mkdirSync8(join29(homedir14(), "terminalhire", "work"), { recursive: true });
38329
38844
  const { createProgress: createProgress2, parseGitProgress: parseGitProgress2, splitProgressChunk: splitProgressChunk2, shStream: shStream2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
38330
38845
  const progress = createProgress2();
38331
38846
  let forkFullName;
@@ -38453,7 +38968,7 @@ function founderPostingIdOf(claim) {
38453
38968
  }
38454
38969
  function sliceWorkDirFor(claimLocalId) {
38455
38970
  const safe = String(claimLocalId).replace(/[^A-Za-z0-9._-]/g, "-");
38456
- return join28(homedir14(), "terminalhire", "work", `slice-${safe}`);
38971
+ return join29(homedir14(), "terminalhire", "work", `slice-${safe}`);
38457
38972
  }
38458
38973
  function assertNoBooleanPath(dest, flagName) {
38459
38974
  const last = String(dest).split(/[\\/]/).filter(Boolean).pop();
@@ -38578,9 +39093,9 @@ function writeSliceFiles(destDir, files) {
38578
39093
  const unavailable = [];
38579
39094
  for (const f of files) {
38580
39095
  if (typeof f.content === "string") {
38581
- const abs = join28(destDir, f.path);
38582
- mkdirSync7(dirname8(abs), { recursive: true });
38583
- writeFileSync15(abs, f.content, "utf8");
39096
+ const abs = join29(destDir, f.path);
39097
+ mkdirSync8(dirname8(abs), { recursive: true });
39098
+ writeFileSync16(abs, f.content, "utf8");
38584
39099
  written.push(f.path);
38585
39100
  } else {
38586
39101
  unavailable.push({ path: f.path, reason: f.unavailableReason || "(no reason given)" });
@@ -38614,7 +39129,7 @@ function writeDeliveredBrief(destDir, spec) {
38614
39129
  function ensureExcludedPackDir(destDir) {
38615
39130
  let occupant = null;
38616
39131
  try {
38617
- occupant = lstatSync3(join28(destDir, BRIEF_DIR));
39132
+ occupant = lstatSync3(join29(destDir, BRIEF_DIR));
38618
39133
  } catch (err) {
38619
39134
  if (err?.code !== "ENOENT") {
38620
39135
  return {
@@ -38629,12 +39144,12 @@ function ensureExcludedPackDir(destDir) {
38629
39144
  reason: `${BRIEF_DIR}/ already exists in the delivered tree, and excluding it would hide that content from your patch`
38630
39145
  };
38631
39146
  }
38632
- const excludeFile = join28(destDir, ".git", "info", "exclude");
39147
+ const excludeFile = join29(destDir, ".git", "info", "exclude");
38633
39148
  try {
38634
39149
  const existing = existsSync13(excludeFile) ? readFileSync14(excludeFile, "utf8") : "";
38635
39150
  if (!existing.split("\n").includes(BRIEF_EXCLUDE_LINE)) {
38636
- mkdirSync7(dirname8(excludeFile), { recursive: true });
38637
- writeFileSync15(
39151
+ mkdirSync8(dirname8(excludeFile), { recursive: true });
39152
+ writeFileSync16(
38638
39153
  excludeFile,
38639
39154
  `${existing}${existing === "" || existing.endsWith("\n") ? "" : "\n"}${BRIEF_EXCLUDE_LINE}
38640
39155
  `,
@@ -38648,9 +39163,9 @@ function ensureExcludedPackDir(destDir) {
38648
39163
  }
38649
39164
  function writePackFile(destDir, relPath, content, what) {
38650
39165
  try {
38651
- const abs = join28(destDir, relPath);
38652
- mkdirSync7(dirname8(abs), { recursive: true });
38653
- writeFileSync15(abs, content, { encoding: "utf8", flag: "wx" });
39166
+ const abs = join29(destDir, relPath);
39167
+ mkdirSync8(dirname8(abs), { recursive: true });
39168
+ writeFileSync16(abs, content, { encoding: "utf8", flag: "wx" });
38654
39169
  } catch (err) {
38655
39170
  return { written: false, reason: `the ${what} could not be written (${err.message})` };
38656
39171
  }
@@ -39065,7 +39580,7 @@ async function cmdSliceFullTier(claims, id, local, fullTierBody, flags, cloneRep
39065
39580
  if (resolvedDir.suffixed) {
39066
39581
  console.log(`terminalhire claim: the usual directory has content \u2014 using ${dest}`);
39067
39582
  }
39068
- mkdirSync7(dest, { recursive: true });
39583
+ mkdirSync8(dest, { recursive: true });
39069
39584
  const branch = `claim/${String(claim.id).replace(/[^A-Za-z0-9._-]/g, "-")}`;
39070
39585
  let engine;
39071
39586
  try {
@@ -39247,7 +39762,7 @@ async function attemptSliceDelivery(id, flags = {}) {
39247
39762
  if (resolvedDir.suffixed) {
39248
39763
  console.log(`terminalhire claim: the usual directory has content \u2014 using ${finalDest}`);
39249
39764
  }
39250
- mkdirSync7(dirname8(finalDest), { recursive: true });
39765
+ mkdirSync8(dirname8(finalDest), { recursive: true });
39251
39766
  let dest = mkdtempSync5(`${finalDest}.tmp-`);
39252
39767
  const { written, unavailable } = writeSliceFiles(dest, body.files);
39253
39768
  const branch = `claim/${String(claim.id).replace(/[^A-Za-z0-9._-]/g, "-")}`;
@@ -39788,7 +40303,7 @@ async function cmdSubmit(id, flags = {}) {
39788
40303
  const head = `${ghUser}:${claim.branch}`;
39789
40304
  const title = flags.title || claim.title;
39790
40305
  const noBody = Boolean(flags["no-body"]);
39791
- const prBodyPath = join28(wt, "PR-BODY.md");
40306
+ const prBodyPath = join29(wt, "PR-BODY.md");
39792
40307
  const bodySource = pickBodySource({
39793
40308
  bodyFileFlag: flags["body-file"],
39794
40309
  noBody,
@@ -40028,7 +40543,7 @@ function readClaimPushMarker() {
40028
40543
  }
40029
40544
  function writeClaimPushMarker(marker) {
40030
40545
  ensureStateDir(TERMINALHIRE_DIR11);
40031
- writeFileSync15(CLAIM_PUSH_MARKER, JSON.stringify(marker, null, 2) + "\n", "utf8");
40546
+ writeFileSync16(CLAIM_PUSH_MARKER, JSON.stringify(marker, null, 2) + "\n", "utf8");
40032
40547
  }
40033
40548
  function clearClaimPushMarker() {
40034
40549
  try {