@zixt/host 0.0.13 → 0.0.15

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 +144 -27
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import { homedir } from "node:os";
31
31
  // package.json
32
32
  var package_default = {
33
33
  name: "@zixt/host",
34
- version: "0.0.13",
34
+ version: "0.0.15",
35
35
  type: "module",
36
36
  exports: {
37
37
  ".": "./src/client.ts",
@@ -14872,6 +14872,16 @@ var MemberApprovalRequestContext = ApprovalRequestContext.extend({
14872
14872
  }),
14873
14873
  acting: ApprovalRequestContext.shape.acting.extend({ providerPrincipal: external_exports.null() })
14874
14874
  });
14875
+ var QuestionChoice = external_exports.object({
14876
+ label: external_exports.string().trim().min(1).max(120),
14877
+ description: external_exports.string().trim().min(1).max(500).optional()
14878
+ }).strict();
14879
+ var QuestionChoices = external_exports.array(QuestionChoice).min(2).max(5).superRefine((choices, ctx) => {
14880
+ const labels = choices.map(({ label }) => label.toLocaleLowerCase());
14881
+ if (new Set(labels).size !== labels.length) {
14882
+ ctx.addIssue({ code: "custom", message: "question choice labels must be unique" });
14883
+ }
14884
+ });
14875
14885
  var Approval = external_exports.object({
14876
14886
  id: ApprovalId,
14877
14887
  orgId: OrgId,
@@ -14882,6 +14892,8 @@ var Approval = external_exports.object({
14882
14892
  summary: external_exports.string().min(1).max(500),
14883
14893
  /** The concrete action payload, rendered verbatim for review (GR-3). */
14884
14894
  payload: external_exports.string().max(5e4),
14895
+ /** Present only when ask_user supplied a bounded multiple-choice question. */
14896
+ questionChoices: QuestionChoices.optional(),
14885
14897
  status: ApprovalStatus,
14886
14898
  deliveryStatus: ApprovalDeliveryStatus,
14887
14899
  /** Why delivery became impossible; null while pending or after delivery. */
@@ -15334,13 +15346,19 @@ var CreateOrgRequest = external_exports.object({
15334
15346
  });
15335
15347
  var DESKTOP_SIDEBAR_MIN_WIDTH_PX = 240;
15336
15348
  var DESKTOP_SIDEBAR_MAX_WIDTH_PX = 420;
15349
+ var TASK_SIDEBAR_MIN_WIDTH_PX = 240;
15350
+ var TASK_SIDEBAR_MAX_WIDTH_PX = 480;
15337
15351
  var DesktopSidebarPreference = external_exports.object({
15338
15352
  collapsed: external_exports.boolean(),
15339
15353
  /** Last expanded width; collapsing never discards the user's chosen width. */
15340
15354
  expandedWidthPx: external_exports.number().int().min(DESKTOP_SIDEBAR_MIN_WIDTH_PX).max(DESKTOP_SIDEBAR_MAX_WIDTH_PX)
15341
15355
  });
15356
+ var TaskSidebarPreference = external_exports.object({
15357
+ expandedWidthPx: external_exports.number().int().min(TASK_SIDEBAR_MIN_WIDTH_PX).max(TASK_SIDEBAR_MAX_WIDTH_PX)
15358
+ });
15342
15359
  var OrgUiPreferences = external_exports.object({
15343
15360
  desktopSidebar: DesktopSidebarPreference,
15361
+ taskSidebar: TaskSidebarPreference,
15344
15362
  /** Missing legacy storage is revision zero. */
15345
15363
  revision: external_exports.number().int().min(0),
15346
15364
  updatedAt: IsoDate2.nullable()
@@ -15349,9 +15367,16 @@ var DesktopSidebarPreferencePatch = DesktopSidebarPreference.partial().refine(
15349
15367
  (value) => Object.keys(value).length > 0,
15350
15368
  "empty desktop sidebar update"
15351
15369
  );
15370
+ var TaskSidebarPreferencePatch = TaskSidebarPreference.partial().refine(
15371
+ (value) => Object.keys(value).length > 0,
15372
+ "empty task sidebar update"
15373
+ );
15352
15374
  var UpdateOrgUiPreferencesRequest = external_exports.object({
15353
15375
  expectedRevision: external_exports.number().int().min(0),
15354
- desktopSidebar: DesktopSidebarPreferencePatch
15376
+ desktopSidebar: DesktopSidebarPreferencePatch.optional(),
15377
+ taskSidebar: TaskSidebarPreferencePatch.optional()
15378
+ }).refine((value) => value.desktopSidebar !== void 0 || value.taskSidebar !== void 0, {
15379
+ message: "at least one layout preference is required"
15355
15380
  });
15356
15381
  var ListOrgsResponse = external_exports.object({
15357
15382
  orgs: external_exports.array(Org.extend({ role: OrgRole, uiPreferences: OrgUiPreferences }))
@@ -15884,7 +15909,34 @@ var MemberTaskOriginProjection = TaskOrigin.pick({
15884
15909
  var MemberTaskCancellationProjection = TaskCancellation.omit({ hostId: true }).extend({
15885
15910
  hostId: external_exports.null()
15886
15911
  });
15887
- var AdminTaskProjection = Task.extend({ metadataRedacted: external_exports.literal(false) });
15912
+ var taskGitSummaryLabel = (maxLength, label) => external_exports.string().min(1).max(maxLength).refine(
15913
+ isSafeSingleLineDisplayText,
15914
+ `${label} must not contain control, line-separator, or bidirectional formatting characters`
15915
+ );
15916
+ var TaskGitSummary = external_exports.object({
15917
+ repositories: external_exports.array(
15918
+ external_exports.object({
15919
+ githubRepository: external_exports.string().regex(/^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/).optional(),
15920
+ branch: taskGitSummaryLabel(512, "branch").nullable(),
15921
+ ahead: external_exports.number().int().min(0),
15922
+ behind: external_exports.number().int().min(0),
15923
+ changedFiles: external_exports.number().int().min(0),
15924
+ pullRequest: external_exports.object({
15925
+ number: external_exports.number().int().min(1),
15926
+ url: external_exports.string().max(2e3).regex(
15927
+ /^https:\/\/github\.com\/[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}\/pull\/[1-9][0-9]*$/
15928
+ ),
15929
+ state: external_exports.enum(["open", "closed", "merged"]),
15930
+ draft: external_exports.boolean()
15931
+ }).strict().optional()
15932
+ }).strict()
15933
+ ).min(1).max(20),
15934
+ observedAt: IsoDate
15935
+ }).strict();
15936
+ var AdminTaskProjection = Task.extend({
15937
+ metadataRedacted: external_exports.literal(false),
15938
+ gitSummary: TaskGitSummary.nullable().optional()
15939
+ });
15888
15940
  var MemberTaskProjection = Task.omit({
15889
15941
  origin: true,
15890
15942
  hostId: true,
@@ -15893,7 +15945,8 @@ var MemberTaskProjection = Task.omit({
15893
15945
  metadataRedacted: external_exports.literal(true),
15894
15946
  origin: MemberTaskOriginProjection,
15895
15947
  hostId: external_exports.null(),
15896
- cancellation: MemberTaskCancellationProjection.nullable().default(null)
15948
+ cancellation: MemberTaskCancellationProjection.nullable().default(null),
15949
+ gitSummary: external_exports.null().optional()
15897
15950
  });
15898
15951
  var TaskProjection = external_exports.discriminatedUnion("metadataRedacted", [
15899
15952
  AdminTaskProjection,
@@ -18414,7 +18467,9 @@ var ApprovalRequest = external_exports.object({
18414
18467
  category: external_exports.string(),
18415
18468
  summary: external_exports.string().min(1).max(500),
18416
18469
  /** Concrete action payload rendered verbatim on the approval card (GR-3). */
18417
- payload: external_exports.string().max(5e4)
18470
+ payload: external_exports.string().max(5e4),
18471
+ /** Optional structured answers for agent.question; absent means free text. */
18472
+ questionChoices: QuestionChoices.optional()
18418
18473
  });
18419
18474
  var JournalAppend = external_exports.object({
18420
18475
  type: external_exports.literal("journal.append"),
@@ -20524,7 +20579,7 @@ var HostClient = class _HostClient {
20524
20579
  const { provider: _provider, ...legacyGrant } = grant;
20525
20580
  return legacyGrant;
20526
20581
  };
20527
- const requestApproval = (category, summary, payload) => {
20582
+ const requestApproval = (category, summary, payload, questionChoices) => {
20528
20583
  if (authorityController.signal.aborted) {
20529
20584
  return Promise.resolve({ approved: false, guidance: "task was cancelled" });
20530
20585
  }
@@ -20536,7 +20591,8 @@ var HostClient = class _HostClient {
20536
20591
  requestId,
20537
20592
  category: safe(category, 200),
20538
20593
  summary: safe(summary, 500),
20539
- payload: safe(payload, 5e4)
20594
+ payload: safe(payload, 5e4),
20595
+ ...questionChoices ? { questionChoices: [...questionChoices] } : {}
20540
20596
  });
20541
20597
  return new Promise((resolve13) => {
20542
20598
  const waiters = this.approvalWaiters.get(cancelKey) ?? /* @__PURE__ */ new Map();
@@ -30668,7 +30724,7 @@ var CADENCE_PROPS = {
30668
30724
  var TOOLS = [
30669
30725
  {
30670
30726
  name: "ask_user",
30671
- 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. The answer arrives as text.",
30727
+ 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.",
30672
30728
  inputSchema: {
30673
30729
  type: "object",
30674
30730
  properties: {
@@ -30676,9 +30732,35 @@ var TOOLS = [
30676
30732
  context: {
30677
30733
  type: "string",
30678
30734
  description: "Optional supporting detail shown alongside the question."
30735
+ },
30736
+ choices: {
30737
+ type: "array",
30738
+ minItems: 2,
30739
+ maxItems: 5,
30740
+ description: "Optional concrete answers. Omit when the person should type freely.",
30741
+ items: {
30742
+ type: "object",
30743
+ properties: {
30744
+ label: {
30745
+ type: "string",
30746
+ minLength: 1,
30747
+ maxLength: 120,
30748
+ description: "Short answer label."
30749
+ },
30750
+ description: {
30751
+ type: "string",
30752
+ minLength: 1,
30753
+ maxLength: 500,
30754
+ description: "Optional consequence or detail that helps the person choose."
30755
+ }
30756
+ },
30757
+ required: ["label"],
30758
+ additionalProperties: false
30759
+ }
30679
30760
  }
30680
30761
  },
30681
- required: ["question"]
30762
+ required: ["question"],
30763
+ additionalProperties: false
30682
30764
  }
30683
30765
  },
30684
30766
  {
@@ -31201,7 +31283,22 @@ function createAskUserServer() {
31201
31283
  }
31202
31284
  try {
31203
31285
  const context = typeof args["context"] === "string" ? args["context"] : "";
31204
- toolText(await handlers.askUser(question, context));
31286
+ const rawChoices = args["choices"];
31287
+ const parsedChoices = rawChoices === void 0 ? void 0 : QuestionChoices.safeParse(rawChoices);
31288
+ if (parsedChoices && !parsedChoices.success) {
31289
+ toolText(
31290
+ "Choices must contain 2-5 unique, non-empty labels with optional descriptions.",
31291
+ true
31292
+ );
31293
+ return;
31294
+ }
31295
+ toolText(
31296
+ await handlers.askUser(
31297
+ question,
31298
+ context,
31299
+ parsedChoices?.success ? parsedChoices.data : void 0
31300
+ )
31301
+ );
31205
31302
  } catch (err) {
31206
31303
  toolText(`The question could not be delivered: ${String(err)}`, true);
31207
31304
  }
@@ -33267,12 +33364,8 @@ function createCliRunner(adapter, opts = {}) {
33267
33364
  try {
33268
33365
  cwd = await requireRealDirectory4(configuredWorkspace, "configured workspace");
33269
33366
  } catch (error52) {
33270
- return {
33271
- outcome: "failed",
33272
- summary: error52 instanceof Error ? error52.message : "configured workspace is unusable",
33273
- summaryEvidence: "host_observed",
33274
- usage: { inputTokens: 0, outputTokens: 0 }
33275
- };
33367
+ const reason = error52 instanceof Error ? error52.message : "configured workspace is unusable";
33368
+ task.event("status", `${reason}. Continuing in the teammate folder instead.`);
33276
33369
  }
33277
33370
  }
33278
33371
  if (task.cancelledNow()) return cancelledBeforeRun();
@@ -33391,13 +33484,14 @@ function createCliRunner(adapter, opts = {}) {
33391
33484
  throw new Error("prepared provider workspace does not match the task assignment");
33392
33485
  }
33393
33486
  askUserServer.register(runToken, {
33394
- askUser: async (question, context) => {
33487
+ askUser: async (question, context, choices) => {
33395
33488
  pendingAsks++;
33396
33489
  try {
33397
33490
  const decision = await task.requestApproval(
33398
33491
  "agent.question",
33399
33492
  question.slice(0, 500),
33400
- (context || question).slice(0, MAX_APPROVAL_PAYLOAD)
33493
+ (context || question).slice(0, MAX_APPROVAL_PAYLOAD),
33494
+ choices
33401
33495
  );
33402
33496
  return decision.guidance ?? (decision.approved ? "Approved. Proceed." : "No. Do not proceed.");
33403
33497
  } finally {
@@ -33426,6 +33520,18 @@ function createCliRunner(adapter, opts = {}) {
33426
33520
  ];
33427
33521
  })
33428
33522
  });
33523
+ const workingContextEnvironments = buildWorkingContextCommandEnvironments({
33524
+ inherited: process.env,
33525
+ runnerEnv: env,
33526
+ ...githubShell ? { githubShellBrokerEnv: githubShell.environment } : {}
33527
+ });
33528
+ const gitDetected = preparedWorkspace !== null || await collectWorkingContext({
33529
+ workingDirectory: cwd,
33530
+ preparedWorkspace,
33531
+ git,
33532
+ env: workingContextEnvironments.git,
33533
+ signal: task.authoritySignal
33534
+ }).then((context) => Boolean(context.repositories?.length)).catch(() => false);
33429
33535
  const prepared = await adapter.prepareRun({
33430
33536
  task,
33431
33537
  runner,
@@ -33436,6 +33542,7 @@ function createCliRunner(adapter, opts = {}) {
33436
33542
  connections: attachedConnections
33437
33543
  },
33438
33544
  preparedWorkspace,
33545
+ gitDetected,
33439
33546
  // The trusted workspace facts describe where Zixt put provider clones,
33440
33547
  // which is always the Zixt-owned root — never a configured workspace.
33441
33548
  taskRoot,
@@ -33467,11 +33574,6 @@ ${attachmentSection}` : prepared.prompt;
33467
33574
  const abortWorkingContext = () => workingContextAbort.abort();
33468
33575
  task.authoritySignal.addEventListener("abort", abortWorkingContext, { once: true });
33469
33576
  const pullRequests = new WorkingContextPullRequestCache();
33470
- const workingContextEnvironments = buildWorkingContextCommandEnvironments({
33471
- inherited: process.env,
33472
- runnerEnv: env,
33473
- ...githubShell ? { githubShellBrokerEnv: githubShell.environment } : {}
33474
- });
33475
33577
  const ghContextCommand = githubShell?.ghCommand ? { ...githubShell.ghCommand, env: workingContextEnvironments.github } : void 0;
33476
33578
  const gitContextCommand = git.available && git.executablePath ? { executablePath: git.executablePath, env: workingContextEnvironments.git } : void 0;
33477
33579
  const reportWorkingContext = (options = {}) => {
@@ -33778,6 +33880,20 @@ function workspaceSystemPrompt(existing, workspacePath, siblings, gitDetected =
33778
33880
  "",
33779
33881
  "Your session ends the moment you finish a reply, and nothing wakes it: no background notification, watcher, or timer will ever re-invoke you. Run long commands (test gates, builds) synchronously and wait for their result inside the tool call. Never end a reply with work still in flight or a promise to report later: whatever you were waiting for dies with the turn. Your final reply is the report of record, written only when the work is actually complete."
33780
33882
  ];
33883
+ if (gitDetected) {
33884
+ lines.push(
33885
+ "",
33886
+ "# Finishing work in Git",
33887
+ "",
33888
+ "This folder is positively detected as a Git repository. Use installed `git` for local source control and, when the remote is GitHub, installed `gh` for pull requests, checks, and merges.",
33889
+ "",
33890
+ "Before editing, inspect the repository and call `list_tasks` to check whether another live Task overlaps this work. Never disturb another Task\u2019s checkout; use an isolated branch and worktree when work may be concurrent.",
33891
+ "",
33892
+ "Unless the human explicitly asked you to stop for review before merging, Git work is not complete merely because files, a branch, or a pull request exist. Update from the latest default branch, resolve ordinary conflicts yourself, run the relevant validation, commit, push, open or update the pull request when the repository uses them, wait for required checks, merge it, and verify the merged state. Clean up temporary worktrees after integration. Do not strand finished work on an unmerged branch.",
33893
+ "",
33894
+ "If repository policy, unavailable authority, an ambiguous product decision, or a conflict you cannot safely resolve prevents completion, use `ask_user` with the exact blocker and required decision. Do not report the Task as done while integration is still pending."
33895
+ );
33896
+ }
33781
33897
  if (siblings.length > 0) {
33782
33898
  const siblingData = promptDescriptiveJson(
33783
33899
  siblings.slice(0, 100).map((sibling) => ({
@@ -34316,7 +34432,8 @@ var claudeCodeAdapter = {
34316
34432
  input.mcp,
34317
34433
  input.preparedWorkspace,
34318
34434
  input.taskRoot,
34319
- input.cwd
34435
+ input.cwd,
34436
+ input.gitDetected
34320
34437
  );
34321
34438
  const sessionId = input.task.spec.sessionKey ?? randomUUID11();
34322
34439
  const observeRuntime = createRuntimeReporter(input, sessionId);
@@ -34367,7 +34484,7 @@ function delay2(ms) {
34367
34484
  timer.unref?.();
34368
34485
  });
34369
34486
  }
34370
- async function buildArgs(task, runner, artifacts, mcp, preparedWorkspace, taskRoot, workspacePath = taskRoot ?? "") {
34487
+ async function buildArgs(task, runner, artifacts, mcp, preparedWorkspace, taskRoot, workspacePath = taskRoot ?? "", gitDetected = false) {
34371
34488
  const args = [
34372
34489
  // Hermetic, non-interactive run — no local slash commands or global state.
34373
34490
  "--disable-slash-commands",
@@ -34387,7 +34504,7 @@ async function buildArgs(task, runner, artifacts, mcp, preparedWorkspace, taskRo
34387
34504
  trustedWorkspaceSystemPrompt(task.spec.system, preparedWorkspace, taskRoot),
34388
34505
  workspacePath,
34389
34506
  task.siblingTasks(),
34390
- preparedWorkspace !== null
34507
+ gitDetected
34391
34508
  );
34392
34509
  if (systemPrompt) {
34393
34510
  const promptPath = await artifacts.writeSystemPrompt(systemPrompt);
@@ -34641,7 +34758,7 @@ function createCodexAdapter(threadIndexRoot) {
34641
34758
  trustedWorkspaceSystemPrompt(task.spec.system, input.preparedWorkspace, input.taskRoot),
34642
34759
  input.cwd,
34643
34760
  task.siblingTasks(),
34644
- input.preparedWorkspace !== null
34761
+ input.gitDetected
34645
34762
  );
34646
34763
  const prompt = platformInstructions ? `<zixt_platform_instructions>
34647
34764
  ${platformInstructions}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zixt/host",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/client.ts",