@zixt/host 0.0.83 → 0.0.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +67 -70
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import { homedir as homedir3 } from "node:os";
31
31
  // package.json
32
32
  var package_default = {
33
33
  name: "@zixt/host",
34
- version: "0.0.83",
34
+ version: "0.0.85",
35
35
  type: "module",
36
36
  exports: {
37
37
  ".": "./src/client.ts",
@@ -16805,6 +16805,7 @@ var ListTasksResponse = external_exports.object({
16805
16805
  // ../../packages/contracts/src/protocol.ts
16806
16806
  var PROTOCOL_VERSION = 8;
16807
16807
  var BROWSER_PROFILE_INVENTORY_PAGE_SIZE = 200;
16808
+ var HELLO_UNWOUND_ASSIGNMENT_LIMIT = 1e3;
16808
16809
  var TASK_CANCEL_ACK_EVENT = "zixt.task.cancel.acknowledged";
16809
16810
  var TASK_CREDENTIAL_ROLLOVER_ACK_EVENT = "zixt.task.credential_rollover.acknowledged";
16810
16811
  var UnwoundAssignmentRef = external_exports.object({
@@ -18051,7 +18052,7 @@ var HelloFrame = external_exports.object({
18051
18052
  * prior socket died. Optional for rolling v5 hosts; repeated references are
18052
18053
  * idempotent and remain presence/host/org/epoch fenced by the cloud.
18053
18054
  */
18054
- unwoundAssignments: external_exports.array(UnwoundAssignmentRef).max(1e3).optional()
18055
+ unwoundAssignments: external_exports.array(UnwoundAssignmentRef).max(HELLO_UNWOUND_ASSIGNMENT_LIMIT).optional()
18055
18056
  });
18056
18057
  var BrowserProfileInventoryFrame = external_exports.object({
18057
18058
  type: external_exports.literal("browser.profile.inventory"),
@@ -18176,6 +18177,8 @@ var CLOSE_CODES = {
18176
18177
  replacedByNewerConnection: 4003,
18177
18178
  revoked: 4004
18178
18179
  };
18180
+ var UNSUPPORTED_PROTOCOL_CLOSE_REASON = "unsupported protocol version";
18181
+ var CLOSE_REASON_MAX_BYTES = 123;
18179
18182
 
18180
18183
  // ../../packages/contracts/src/manager.ts
18181
18184
  var MANAGER_DEFAULT_MODEL = "gpt-5.6-luna";
@@ -21315,6 +21318,17 @@ function operationGrantRetryCause(code) {
21315
21318
  }
21316
21319
  return "GitHub is temporarily unavailable for credential issuance";
21317
21320
  }
21321
+ function closeDetail(reason) {
21322
+ if (reason === void 0) return void 0;
21323
+ const raw = typeof reason === "string" ? reason : reason.toString("utf8");
21324
+ let printable = "";
21325
+ for (const character of raw) {
21326
+ const code = character.codePointAt(0) ?? 0;
21327
+ printable += code < 32 || code === 127 ? " " : character;
21328
+ }
21329
+ const text = printable.trim().slice(0, CLOSE_REASON_MAX_BYTES);
21330
+ return text.length > 0 ? text : void 0;
21331
+ }
21318
21332
  var HostClient = class _HostClient {
21319
21333
  constructor(opts) {
21320
21334
  this.opts = opts;
@@ -21687,9 +21701,13 @@ var HostClient = class _HostClient {
21687
21701
  const inventoryConfigured = Boolean(
21688
21702
  this.opts.browser?.profileInventoryPages || this.opts.browser?.profileInventory
21689
21703
  );
21690
- const unwoundAssignments = [...this.pendingUnwoundAssignments.values()].sort(
21704
+ const disclaimable = [...this.pendingUnwoundAssignments.values()].sort(
21691
21705
  (left, right) => left.taskId === right.taskId ? left.epoch - right.epoch : left.taskId.localeCompare(right.taskId)
21692
21706
  );
21707
+ const unwoundAssignments = disclaimable.slice(0, HELLO_UNWOUND_ASSIGNMENT_LIMIT);
21708
+ if (disclaimable.length > unwoundAssignments.length) {
21709
+ this.opts.onDisclaimerBacklog?.(disclaimable.length, unwoundAssignments.length);
21710
+ }
21693
21711
  this.send(
21694
21712
  {
21695
21713
  type: "hello",
@@ -21721,14 +21739,14 @@ var HostClient = class _HostClient {
21721
21739
  if (this.ws === ws) ws.terminate();
21722
21740
  });
21723
21741
  });
21724
- ws.on("close", (code) => {
21742
+ ws.on("close", (code, reason) => {
21725
21743
  if (inventoryRetryTimer) clearTimeout(inventoryRetryTimer);
21726
- void this.handleClose(ws, code, closeReason, frameTail);
21744
+ void this.handleClose(ws, code, closeReason, frameTail, closeDetail(reason));
21727
21745
  });
21728
21746
  ws.on("error", () => {
21729
21747
  });
21730
21748
  }
21731
- async handleClose(ws, code, closeReason, frameTail = Promise.resolve()) {
21749
+ async handleClose(ws, code, closeReason, frameTail = Promise.resolve(), detail) {
21732
21750
  if (this.ws !== ws) return;
21733
21751
  this.ws = void 0;
21734
21752
  this.protocolReady = false;
@@ -21767,13 +21785,18 @@ var HostClient = class _HostClient {
21767
21785
  await this.unwindAllRuns("host connection was replaced by a newer client", "client_shutdown");
21768
21786
  return;
21769
21787
  }
21770
- if (code === CLOSE_CODES.protocolError) {
21771
- this.opts.onStatus?.("incompatible");
21788
+ const versionMismatch = code === CLOSE_CODES.protocolError && detail === UNSUPPORTED_PROTOCOL_CLOSE_REASON;
21789
+ if (versionMismatch) {
21790
+ this.opts.onStatus?.("incompatible", detail);
21772
21791
  this.stopped = true;
21773
21792
  await this.unwindAllRuns("host protocol is incompatible", "client_shutdown");
21774
21793
  return;
21775
21794
  }
21776
- this.opts.onStatus?.(closeReason === "heartbeat_timeout" ? "unresponsive" : "disconnected");
21795
+ if (code === CLOSE_CODES.protocolError) {
21796
+ this.opts.onStatus?.("rejected", detail);
21797
+ } else {
21798
+ this.opts.onStatus?.(closeReason === "heartbeat_timeout" ? "unresponsive" : "disconnected");
21799
+ }
21777
21800
  if (this.stopped) return;
21778
21801
  await this.unwindAllRuns(
21779
21802
  closeReason === "heartbeat_timeout" ? "Zixt Cloud stopped answering heartbeat probes" : "host connection was lost",
@@ -28442,10 +28465,11 @@ var BLOCKED_STATEMENT = /\b(?:you (?:must|need to) (?:be )?(?:sign|log)(?:ged)?
28442
28465
  function authenticationWallGuidance(origin) {
28443
28466
  return [
28444
28467
  `This page is asking someone to sign in to ${origin}.`,
28445
- "You cannot type a password: if a saved website-login Credential covers this site, call browser_login with its name.",
28468
+ "If a saved website-login Credential covers this site, call browser_login with its name.",
28446
28469
  "Otherwise stop here and use ask_user to ask the person to sign in at the Browser panel, naming the Browser in the question so their panel opens.",
28470
+ "That is the route to ask for, because a saved login is filled by Host code, is never shown to you, and is scrubbed from anything you write.",
28447
28471
  "This browser and its profile outlive the run, so a person can sign in after your session checkpoints and you continue on the authenticated page.",
28448
- "Never ask for, accept, or type a password, token, or one-time code in chat.",
28472
+ "A one-time code expires and a person is faster at their own password prompt, so do not ask for either instead of the handoff; if they choose to send you a credential anyway, use it without repeating it.",
28449
28473
  "If this Task cannot be verified without being signed in, ask now rather than reporting that limitation once you have already declared the work verified."
28450
28474
  ].join(" ");
28451
28475
  }
@@ -34233,53 +34257,6 @@ function renderAttachmentSection(files) {
34233
34257
 
34234
34258
  // src/runners/ask-user-server.ts
34235
34259
  import { createServer as createServer2 } from "node:http";
34236
-
34237
- // src/runners/ask-user-secrets.ts
34238
- var SECRET_NOUN_SOURCE = "(?:passwords?|passphrases?|api[ -]?keys?|access tokens?|auth tokens?|api tokens?|bearer tokens?|refresh tokens?|tokens?|secret keys?|client secrets?|private keys?|one[- ]time (?:code|password)s?|otps?|2fa codes?|mfa codes?|verification codes?|security codes?|session cookies?)";
34239
- var SECRET_NOUN = new RegExp(String.raw`\b${SECRET_NOUN_SOURCE}\b`, "i");
34240
- var SECRET_ENV_NAME = /\b[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*_(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PAT)\b/;
34241
- function namesSecret(sentence) {
34242
- return SECRET_NOUN.test(sentence) || SECRET_ENV_NAME.test(sentence);
34243
- }
34244
- var TRANSFER = "(?:paste|send|share|provide|supply|give|reply|respond|forward|drop|tell)";
34245
- var REQUEST_PATTERNS = [
34246
- new RegExp(String.raw`^\s*(?:please\s+)?${TRANSFER}\b`, "i"),
34247
- new RegExp(String.raw`\b(?:can|could|would|will)\s+you\s+(?:please\s+)?${TRANSFER}\b`, "i"),
34248
- new RegExp(
34249
- String.raw`\b${TRANSFER}\b[^.?!]{0,40}\b(?:me|here|in chat|in the chat|in your (?:reply|answer|message)|below)\b`,
34250
- "i"
34251
- ),
34252
- new RegExp(String.raw`\b${TRANSFER}\b[^.?!]{0,20}\byour\b`, "i"),
34253
- new RegExp(
34254
- String.raw`\bwhat(?:'s| is|s|\s+are)\s+(?:the|your)\s+(?:[\w-]+\s+){0,2}${SECRET_NOUN_SOURCE}\b`,
34255
- "i"
34256
- )
34257
- ];
34258
- var NEGATION = /\b(?:do not|don'?t|never|no need|without|rather than|instead of|avoid)\b/i;
34259
- function namesSafeDestination(sentence) {
34260
- return /\bbrowser\b/i.test(sentence) || /\bcredentials?\s+(?:page|screen|tab|section|store|settings|panel|vault)\b/i.test(sentence) || /\b(?:add|adds|added|adding|store|stored|save|saved|create|created|put)\b[^.?!]{0,60}\bcredential/i.test(
34261
- sentence
34262
- );
34263
- }
34264
- var SECRET_IN_CHAT_REFUSAL = [
34265
- "Not delivered: Zixt never carries a password, token, or one-time code through chat (W5).",
34266
- "Ask for the capability instead of the value.",
34267
- "For a website, ask the person to sign in themselves at the Browser panel, or to add a website-login Credential whose name you can pass to browser_login.",
34268
- "For anything else, ask them to add it under Resources \u2192 Credentials as an UPPER_SNAKE_CASE name, which reaches your next run as an environment variable.",
34269
- "Then ask again without requesting the value."
34270
- ].join(" ");
34271
- function secretSolicitationRefusal(question, context) {
34272
- const sentences = `${question}
34273
- ${context}`.split(/(?<=[.!?\n])\s+/).map((sentence) => sentence.trim()).filter((sentence) => sentence.length > 0);
34274
- for (const sentence of sentences) {
34275
- if (!namesSecret(sentence)) continue;
34276
- if (NEGATION.test(sentence) || namesSafeDestination(sentence)) continue;
34277
- if (REQUEST_PATTERNS.some((pattern) => pattern.test(sentence))) return SECRET_IN_CHAT_REFUSAL;
34278
- }
34279
- return null;
34280
- }
34281
-
34282
- // src/runners/ask-user-server.ts
34283
34260
  var CADENCE_PROPS = {
34284
34261
  cadence_kind: {
34285
34262
  type: "string",
@@ -34299,7 +34276,7 @@ var CADENCE_PROPS = {
34299
34276
  var TOOLS = [
34300
34277
  {
34301
34278
  name: "ask_user",
34302
- description: "Ask the human supervising this task a question and wait for their answer. Use it whenever you need a decision, missing context, or plan approval before proceeding. When there are 2-5 concrete alternatives, provide them as choices; the person can still enter another answer. The answer arrives as text.",
34279
+ description: "Ask the human supervising this task a question and wait for their answer. Use it whenever you need a decision, missing context, or plan approval before proceeding. When there are 2-5 concrete alternatives, provide them as choices; the person can still enter another answer. The answer arrives as text. For a credential, ask for the capability rather than the value: a Credential added under Resources reaches your next run as an environment variable and is scrubbed from everything you write, and a website sign-in belongs at the Browser panel. If the person chooses to send you a value here anyway, use it for this task and store it with set_secret if it should persist, but never print it, write it into a file you commit, or repeat it in your report.",
34303
34280
  inputSchema: {
34304
34281
  type: "object",
34305
34282
  properties: {
@@ -34997,11 +34974,6 @@ function createAskUserServer() {
34997
34974
  }
34998
34975
  try {
34999
34976
  const context = typeof args["context"] === "string" ? args["context"] : "";
35000
- const refusal = secretSolicitationRefusal(question, context);
35001
- if (refusal) {
35002
- toolText(refusal, true);
35003
- return;
35004
- }
35005
34977
  const rawChoices = args["choices"];
35006
34978
  const parsedChoices = rawChoices === void 0 ? void 0 : QuestionChoices.safeParse(rawChoices);
35007
34979
  if (parsedChoices && !parsedChoices.success) {
@@ -40411,16 +40383,26 @@ var colorOverride = forcedColor(cliOptions.color) ?? (process.env[HOST_CONSOLE_C
40411
40383
  var hostConsoleTail = [];
40412
40384
  var hostConsoleSequence = 0;
40413
40385
  var clientForConsole = {};
40386
+ function recordConsoleLine(line) {
40387
+ const entry = { sequence: ++hostConsoleSequence, line };
40388
+ hostConsoleTail.push(entry);
40389
+ if (hostConsoleTail.length > 200) hostConsoleTail.shift();
40390
+ clientForConsole.current?.hostConsoleLine(entry);
40391
+ }
40414
40392
  var log = createHostLogger({
40415
40393
  ...colorOverride === void 0 ? {} : { color: colorOverride },
40416
40394
  write: (line) => {
40417
40395
  console.error(line);
40418
- const entry = { sequence: ++hostConsoleSequence, line };
40419
- hostConsoleTail.push(entry);
40420
- if (hostConsoleTail.length > 200) hostConsoleTail.shift();
40421
- clientForConsole.current?.hostConsoleLine(entry);
40396
+ recordConsoleLine(line);
40422
40397
  }
40423
40398
  });
40399
+ function replayConsoleLine(level, message, context) {
40400
+ recordConsoleLine(
40401
+ formatHostLogLine(level, message, context, {
40402
+ ...colorOverride === void 0 ? {} : { color: colorOverride }
40403
+ })
40404
+ );
40405
+ }
40424
40406
  if (cliOptions.help) {
40425
40407
  process.stdout.write(`${hostHelpText()}
40426
40408
  `);
@@ -40805,6 +40787,14 @@ var client = new HostClient({
40805
40787
  forgetAcknowledgedAcceptedAssignments(assignments, acceptedAssignmentRoot)
40806
40788
  ]);
40807
40789
  },
40790
+ onDisclaimerBacklog: (pending, reported) => {
40791
+ log.warn("More unfinished Task claims than one connection can report", {
40792
+ machine,
40793
+ pending,
40794
+ reported,
40795
+ next: "The rest are reported as this Machine reconnects; report this if it does not clear"
40796
+ });
40797
+ },
40808
40798
  persistAcceptedAssignment: (assignment) => recordAcceptedAssignment(assignment, acceptedAssignmentRoot).then(() => void 0),
40809
40799
  forgetAcceptedAssignment: (assignment) => forgetAcceptedAssignment(assignment, acceptedAssignmentRoot),
40810
40800
  persistTerminalOutcome: (hostId, outcome) => recordTerminalOutcome(hostId, outcome, terminalOutcomeRoot),
@@ -40836,7 +40826,7 @@ var client = new HostClient({
40836
40826
  onUsableTelemetryReport: () => {
40837
40827
  reportWorkerReadiness();
40838
40828
  },
40839
- onStatus: (status) => {
40829
+ onStatus: (status, detail) => {
40840
40830
  if (status === lastReportedStatus) return;
40841
40831
  lastReportedStatus = status;
40842
40832
  switch (status) {
@@ -40876,6 +40866,13 @@ var client = new HostClient({
40876
40866
  });
40877
40867
  queueMicrotask(() => shutdown(packagedBuild ? UPDATE_EXIT_CODE : DO_NOT_RESTART_EXIT_CODE));
40878
40868
  break;
40869
+ case "rejected":
40870
+ log.error("Zixt Cloud refused this Machine", {
40871
+ ...connectionContext,
40872
+ reason: detail ?? "the cloud gave no reason",
40873
+ next: "Zixt keeps retrying; report this reason if the Machine stays offline, because no update changes it"
40874
+ });
40875
+ break;
40879
40876
  }
40880
40877
  },
40881
40878
  executor,
@@ -40893,7 +40890,7 @@ for (const { record: record2 } of reportedWorkerExits) {
40893
40890
  ...context,
40894
40891
  next: "This Machine restarted itself; report it if the same exit repeats"
40895
40892
  });
40896
- for (const line of record2.stderr) log.error("Previous worker output", { machine, line });
40893
+ for (const line of record2.stderr) replayConsoleLine("error", "Previous worker output", { line });
40897
40894
  }
40898
40895
  log.info("Starting Zixt Host", connectionContext);
40899
40896
  client.start();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zixt/host",
3
- "version": "0.0.83",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/client.ts",