@nyte-ai/plugin 0.0.25-nightly.20261101.43 → 0.0.27

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 (45) hide show
  1. package/THIRD-PARTY-NOTICES.md +65 -0
  2. package/dist/chunks/{bash-description-myvnmtt2.js → bash-description-03bm07ft.js} +2 -8
  3. package/dist/chunks/{bash-description-6mm9fym2.js → bash-description-0gnxcq7q.js} +31 -11
  4. package/dist/chunks/bash-description-0vfhp1qd.js +54 -0
  5. package/dist/chunks/{bash-description-g7gfy7nw.js → bash-description-4kqkzdkp.js} +3 -5
  6. package/dist/chunks/{bash-description-axnga0jz.js → bash-description-a99pev5h.js} +2 -2
  7. package/dist/chunks/{bash-description-bkhr9zrz.js → bash-description-s6v2m3ch.js} +28 -10
  8. package/dist/chunks/{bash-description-qwyy182r.js → bash-description-spc5av5d.js} +1 -7
  9. package/dist/chunks/bash-description-t3hbavcc.js +12 -0
  10. package/dist/src/acp/connection.js +1 -1
  11. package/dist/src/acp/index.js +2 -2
  12. package/dist/src/acp/transcript.js +1 -1
  13. package/dist/src/approvals/approvals.js +6 -3
  14. package/dist/src/approvals/schema.js +1 -1
  15. package/dist/src/browser-client.d.ts +135 -0
  16. package/dist/src/browser-client.js +374 -0
  17. package/dist/src/canvas/canvas-client.d.ts +71 -0
  18. package/dist/src/canvas/canvas-client.js +70 -0
  19. package/dist/src/canvas/canvas-compile.d.ts +0 -34
  20. package/dist/src/canvas/canvas-libraries.d.ts +0 -6
  21. package/dist/src/canvas/canvas-preview.d.ts +0 -65
  22. package/dist/src/canvas/canvas-protocol.d.ts +5 -80
  23. package/dist/src/canvas/canvas.d.ts +0 -25
  24. package/dist/src/canvas/schema.d.ts +0 -37
  25. package/dist/src/client/notifications.js +2 -1
  26. package/dist/src/client/presentation.d.ts +3 -23
  27. package/dist/src/client/presentation.js +7 -3
  28. package/dist/src/client/transcript.js +18 -18
  29. package/dist/src/client/user-messages.d.ts +41 -0
  30. package/dist/src/client/user-messages.js +10 -0
  31. package/dist/src/desktop-settings/desktop-settings.d.ts +31 -0
  32. package/dist/src/desktop-settings/desktop-settings.js +136 -0
  33. package/dist/src/desktop-settings/schema.d.ts +42 -0
  34. package/dist/src/desktop-settings/schema.js +14 -0
  35. package/dist/src/question/client/questions-tui.js +6 -2
  36. package/dist/src/question/client/questions.js +47 -46
  37. package/dist/src/question/question.d.ts +37 -6
  38. package/dist/src/question/question.js +69 -17
  39. package/dist/src/question/schema.d.ts +73 -11
  40. package/dist/src/question/schema.js +9 -5
  41. package/dist/src/rename.d.ts +1 -1
  42. package/dist/src/subagents.js +11 -23
  43. package/dist/src/web/fetch.js +1 -1
  44. package/dist/src/web/index.js +1 -1
  45. package/package.json +30 -14
@@ -0,0 +1,42 @@
1
+ import { Type } from "typebox";
2
+ import type { Static } from "typebox";
3
+ /** How a chat's state can reach you past its sidebar mark, which always shows. */
4
+ export declare const ALERT_CHANNELS: readonly ["notification", "badge", "sound", "bounce", "remind"];
5
+ export type AlertChannel = (typeof ALERT_CHANNELS)[number];
6
+ /** The states that alert, each with the channels it offers. Working only marks the sidebar. */
7
+ export declare const ALERT_OFFERS: {
8
+ readonly waiting: readonly ["notification", "badge", "sound", "bounce", "remind"];
9
+ readonly failed: readonly ["notification", "badge", "sound", "bounce"];
10
+ readonly done: readonly ["notification", "badge", "sound"];
11
+ };
12
+ export type AlertState = keyof typeof ALERT_OFFERS;
13
+ /** The desktop setting holding each state's chosen channels. */
14
+ export declare const ALERT_SETTING: {
15
+ readonly waiting: "alertsWaiting";
16
+ readonly failed: "alertsFailed";
17
+ readonly done: "alertsDone";
18
+ };
19
+ /** Settings the desktop's main and host processes read; the app reads and writes them as `desktop.<key>`. */
20
+ export declare const DESKTOP_SETTING_SCHEMAS: {
21
+ keepAwake: Type.TBoolean;
22
+ pauseOnSleep: Type.TBoolean;
23
+ terminalShell: Type.TUnion<[Type.TString, Type.TNull]>;
24
+ blockAds: Type.TBoolean;
25
+ adblockAllowedHosts: Type.TArray<Type.TString>;
26
+ upgradeToHttps: Type.TBoolean;
27
+ webOrigins: Type.TArray<Type.TString>;
28
+ notifications: Type.TBoolean;
29
+ dockBadge: Type.TBoolean;
30
+ notificationSounds: Type.TBoolean;
31
+ alertsWaiting: Type.TArray<Type.TEnum<["notification", "badge", "sound", "bounce", "remind"]>>;
32
+ alertsFailed: Type.TArray<Type.TEnum<["notification", "badge", "sound", "bounce"]>>;
33
+ alertsDone: Type.TArray<Type.TEnum<["notification", "badge", "sound"]>>;
34
+ remindMinutes: Type.TUnion<[Type.TLiteral<1>, Type.TLiteral<2>, Type.TLiteral<5>, Type.TLiteral<15>]>;
35
+ notifyViewedChat: Type.TBoolean;
36
+ notifySubagents: Type.TBoolean;
37
+ terminalNotifications: Type.TBoolean;
38
+ };
39
+ export type DesktopSettings = {
40
+ readonly [K in keyof typeof DESKTOP_SETTING_SCHEMAS]: Static<(typeof DESKTOP_SETTING_SCHEMAS)[K]>;
41
+ };
42
+ export declare const DEFAULT_DESKTOP_SETTINGS: DesktopSettings;
@@ -0,0 +1,14 @@
1
+ import {
2
+ ALERT_CHANNELS2,
3
+ ALERT_OFFERS2,
4
+ ALERT_SETTING2,
5
+ DESKTOP_SETTING_SCHEMAS2,
6
+ DEFAULT_DESKTOP_SETTINGS2
7
+ } from "../../chunks/bash-description-0vfhp1qd.js";
8
+ export {
9
+ ALERT_CHANNELS2 as ALERT_CHANNELS,
10
+ ALERT_OFFERS2 as ALERT_OFFERS,
11
+ ALERT_SETTING2 as ALERT_SETTING,
12
+ DEFAULT_DESKTOP_SETTINGS2 as DEFAULT_DESKTOP_SETTINGS,
13
+ DESKTOP_SETTING_SCHEMAS2 as DESKTOP_SETTING_SCHEMAS
14
+ };
@@ -1,13 +1,17 @@
1
1
  import {
2
2
  question2
3
- } from "../../../chunks/bash-description-bkhr9zrz.js";
3
+ } from "../../../chunks/bash-description-s6v2m3ch.js";
4
4
 
5
5
  // src/question/client/questions-tui.ts
6
6
  import { Box, Text } from "@opentui/core";
7
7
  import { defineRenderer } from "@nyte-ai/plugin/client";
8
8
  var unreadable = (kind) => Text({ content: `This ${kind} part cannot be shown.` });
9
9
  var questionRenderers = [
10
- defineRenderer(question2, "tui", (state) => Box({ border: true, title: "Questions", flexDirection: "column" }, ...state.questions.map((item, index) => Box({ flexDirection: "column" }, Text({ content: `${item.header}: ${item.question}` }), ...item.options.map((option) => Text({ content: `${option.label}: ${option.description}` })), ...state.answers === null ? [] : [Text({ content: `Answer: ${state.answers[index].join(", ") || "Unanswered"}` })]))), unreadable)
10
+ defineRenderer(question2, "tui", (state) => Box({ border: true, title: "Questions", flexDirection: "column" }, ...state.questions.map((item, index) => Box({ flexDirection: "column" }, Text({ content: `${item.header}: ${item.question}` }), ...item.options.map((option) => Text({ content: `${option.label}: ${option.description}` })), ...state.phase.kind === "answered" ? [
11
+ Text({
12
+ content: `Answer: ${state.phase.answers[index].join(", ") || "Unanswered"}`
13
+ })
14
+ ] : state.phase.kind === "skipped" ? [Text({ content: "Skipped" })] : []))), unreadable)
11
15
  ];
12
16
  var questions_tui_default = {
13
17
  id: "questions-tui",
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  question2
3
- } from "../../../chunks/bash-description-bkhr9zrz.js";
3
+ } from "../../../chunks/bash-description-s6v2m3ch.js";
4
4
 
5
5
  // src/question/client/questions.tsx
6
6
  import { useId, useRef, useState } from "react";
7
7
  import { defineRenderer } from "@nyte-ai/plugin/client";
8
- import { jsxDEV } from "react/jsx-dev-runtime";
9
- var unreadable = (kind) => /* @__PURE__ */ jsxDEV("div", {
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var unreadable = (kind) => /* @__PURE__ */ jsxs("div", {
10
10
  "data-part": kind,
11
11
  children: [
12
12
  "This ",
13
13
  kind,
14
14
  " part cannot be shown."
15
15
  ]
16
- }, undefined, true, undefined, this);
16
+ });
17
17
  function QuestionForm({
18
18
  questions,
19
19
  act
@@ -47,7 +47,7 @@ function QuestionForm({
47
47
  }
48
48
  submitting.current = false;
49
49
  };
50
- return /* @__PURE__ */ jsxDEV("form", {
50
+ return /* @__PURE__ */ jsxs("form", {
51
51
  "data-part": "ask/question",
52
52
  "aria-label": "Questions",
53
53
  onSubmit: (event) => {
@@ -55,18 +55,18 @@ function QuestionForm({
55
55
  submit();
56
56
  },
57
57
  children: [
58
- questions.map((item, index) => /* @__PURE__ */ jsxDEV("fieldset", {
58
+ questions.map((item, index) => /* @__PURE__ */ jsxs("fieldset", {
59
59
  disabled: blocked,
60
60
  children: [
61
- /* @__PURE__ */ jsxDEV("legend", {
61
+ /* @__PURE__ */ jsx("legend", {
62
62
  children: item.header
63
- }, undefined, false, undefined, this),
64
- /* @__PURE__ */ jsxDEV("p", {
63
+ }),
64
+ /* @__PURE__ */ jsx("p", {
65
65
  children: item.question
66
- }, undefined, false, undefined, this),
67
- item.options.map((option) => /* @__PURE__ */ jsxDEV("label", {
66
+ }),
67
+ item.options.map((option) => /* @__PURE__ */ jsxs("label", {
68
68
  children: [
69
- /* @__PURE__ */ jsxDEV("input", {
69
+ /* @__PURE__ */ jsx("input", {
70
70
  type: item.multiple ? "checkbox" : "radio",
71
71
  name: `${id}-${index}`,
72
72
  value: option.label,
@@ -82,19 +82,19 @@ function QuestionForm({
82
82
  if (!item.multiple)
83
83
  setCustom((current) => current.map((typed, position) => position === index ? "" : typed));
84
84
  }
85
- }, undefined, false, undefined, this),
86
- /* @__PURE__ */ jsxDEV("span", {
85
+ }),
86
+ /* @__PURE__ */ jsx("span", {
87
87
  children: option.label
88
- }, undefined, false, undefined, this),
89
- /* @__PURE__ */ jsxDEV("span", {
88
+ }),
89
+ /* @__PURE__ */ jsx("span", {
90
90
  children: option.description
91
- }, undefined, false, undefined, this)
91
+ })
92
92
  ]
93
- }, option.label, true, undefined, this)),
94
- /* @__PURE__ */ jsxDEV("label", {
93
+ }, option.label)),
94
+ /* @__PURE__ */ jsxs("label", {
95
95
  children: [
96
96
  "Type your own answer",
97
- /* @__PURE__ */ jsxDEV("textarea", {
97
+ /* @__PURE__ */ jsx("textarea", {
98
98
  value: custom[index],
99
99
  onChange: ({ currentTarget }) => {
100
100
  const typed = currentTarget.value;
@@ -102,55 +102,56 @@ function QuestionForm({
102
102
  if (!item.multiple && typed.trim() !== "")
103
103
  setSelected((current) => current.map((choices, position) => position === index ? [] : choices));
104
104
  }
105
- }, undefined, false, undefined, this)
105
+ })
106
106
  ]
107
- }, undefined, true, undefined, this)
107
+ })
108
108
  ]
109
- }, index, true, undefined, this)),
110
- /* @__PURE__ */ jsxDEV("button", {
109
+ }, index)),
110
+ /* @__PURE__ */ jsx("button", {
111
111
  type: "submit",
112
112
  disabled: blocked,
113
113
  children: status.kind === "sending" ? "Sending answers…" : "Submit answers"
114
- }, undefined, false, undefined, this),
115
- status.kind === "failed" && /* @__PURE__ */ jsxDEV("p", {
114
+ }),
115
+ status.kind === "failed" && /* @__PURE__ */ jsx("p", {
116
116
  role: "alert",
117
117
  children: status.message
118
- }, undefined, false, undefined, this),
119
- status.kind === "sent" && /* @__PURE__ */ jsxDEV("p", {
118
+ }),
119
+ status.kind === "sent" && /* @__PURE__ */ jsx("p", {
120
120
  role: "status",
121
121
  children: "Answers sent."
122
- }, undefined, false, undefined, this)
122
+ })
123
123
  ]
124
- }, undefined, true, undefined, this);
124
+ });
125
125
  }
126
126
  var questionRenderers = [
127
127
  defineRenderer(question2, "react", (state, act) => {
128
- const answers = state.answers;
129
- if (answers !== null)
130
- return /* @__PURE__ */ jsxDEV("div", {
128
+ const phase = state.phase;
129
+ if (phase.kind === "answered" || phase.kind === "skipped")
130
+ return /* @__PURE__ */ jsx("div", {
131
131
  "data-part": "ask/question",
132
132
  "aria-label": "Answers",
133
- children: state.questions.map((item, index) => /* @__PURE__ */ jsxDEV("section", {
133
+ children: state.questions.map((item, index) => /* @__PURE__ */ jsxs("section", {
134
134
  children: [
135
- /* @__PURE__ */ jsxDEV("h3", {
135
+ /* @__PURE__ */ jsx("h3", {
136
136
  children: item.header
137
- }, undefined, false, undefined, this),
138
- /* @__PURE__ */ jsxDEV("p", {
137
+ }),
138
+ /* @__PURE__ */ jsx("p", {
139
139
  children: item.question
140
- }, undefined, false, undefined, this),
141
- /* @__PURE__ */ jsxDEV("p", {
140
+ }),
141
+ /* @__PURE__ */ jsxs("p", {
142
142
  children: [
143
- "Answer: ",
144
- answers[index].join(", ") || "Unanswered"
143
+ "Answer:",
144
+ " ",
145
+ phase.kind === "answered" ? phase.answers[index].join(", ") || "Unanswered" : "Skipped"
145
146
  ]
146
- }, undefined, true, undefined, this)
147
+ })
147
148
  ]
148
- }, index, true, undefined, this))
149
- }, undefined, false, undefined, this);
150
- return /* @__PURE__ */ jsxDEV(QuestionForm, {
149
+ }, index))
150
+ });
151
+ return /* @__PURE__ */ jsx(QuestionForm, {
151
152
  questions: state.questions,
152
153
  act
153
- }, JSON.stringify(state.questions), false, undefined, this);
154
+ }, JSON.stringify(state.questions));
154
155
  }, unreadable)
155
156
  ];
156
157
  var questions_default = { id: "questions", contribute: { renderers: questionRenderers } };
@@ -1,5 +1,6 @@
1
1
  import type { ToolDefinition } from "@nyte-ai/core";
2
2
  export declare const question: import("@nyte-ai/core").PartDefinition<{
3
+ call: import("@nyte-ai/schema").Id<"call">;
3
4
  questions: {
4
5
  question: string;
5
6
  header: string;
@@ -9,17 +10,33 @@ export declare const question: import("@nyte-ai/core").PartDefinition<{
9
10
  }[];
10
11
  multiple?: boolean | undefined;
11
12
  }[];
12
- answers: string[][] | null;
13
+ phase: {
14
+ kind: "asking";
15
+ } | {
16
+ kind: "open";
17
+ head: import("@nyte-ai/schema").Id<"head">;
18
+ } | {
19
+ kind: "answered";
20
+ answers: string[][];
21
+ head: import("@nyte-ai/schema").Id<"head"> | null;
22
+ } | {
23
+ kind: "skipped";
24
+ late: boolean;
25
+ };
13
26
  }, {
14
27
  answer: {
15
28
  input: import("@nyte-ai/schema").Schema<{
16
29
  answers: string[][];
17
30
  }>;
18
31
  };
32
+ skip: {
33
+ input: import("@nyte-ai/schema").Schema<object>;
34
+ };
19
35
  }>;
20
36
  /**
21
37
  * Contributes the question tool and its grouped modal. The durable wait
22
- * resumes after one complete submission from any client.
38
+ * resumes after one complete submission or a skip from any client, or after
39
+ * a minute; an answer after that reaches the asking head as a message.
23
40
  *
24
41
  * @category plugins
25
42
  * @since 0.1.0
@@ -28,6 +45,7 @@ declare const _default: {
28
45
  id: string;
29
46
  contribute: {
30
47
  parts: import("@nyte-ai/core").PartDefinition<{
48
+ call: import("@nyte-ai/schema").Id<"call">;
31
49
  questions: {
32
50
  question: string;
33
51
  header: string;
@@ -37,13 +55,28 @@ declare const _default: {
37
55
  }[];
38
56
  multiple?: boolean | undefined;
39
57
  }[];
40
- answers: string[][] | null;
58
+ phase: {
59
+ kind: "asking";
60
+ } | {
61
+ kind: "open";
62
+ head: import("@nyte-ai/schema").Id<"head">;
63
+ } | {
64
+ kind: "answered";
65
+ answers: string[][];
66
+ head: import("@nyte-ai/schema").Id<"head"> | null;
67
+ } | {
68
+ kind: "skipped";
69
+ late: boolean;
70
+ };
41
71
  }, {
42
72
  answer: {
43
73
  input: import("@nyte-ai/schema").Schema<{
44
74
  answers: string[][];
45
75
  }>;
46
76
  };
77
+ skip: {
78
+ input: import("@nyte-ai/schema").Schema<object>;
79
+ };
47
80
  }>[];
48
81
  tools: ToolDefinition<{
49
82
  questions: {
@@ -55,9 +88,7 @@ declare const _default: {
55
88
  }[];
56
89
  multiple?: boolean | undefined;
57
90
  }[];
58
- }, {
59
- answers: string[][] | null;
60
- }>[];
91
+ }, unknown>[];
61
92
  };
62
93
  };
63
94
  export default _default;
@@ -1,13 +1,14 @@
1
1
  import {
2
+ QUESTION_TOOL2,
2
3
  answerInput2,
3
- questionOutput2,
4
4
  questionParameters2,
5
5
  question2
6
- } from "../../chunks/bash-description-bkhr9zrz.js";
6
+ } from "../../chunks/bash-description-s6v2m3ch.js";
7
7
 
8
8
  // src/question/question.ts
9
9
  import { definePart, dev } from "@nyte-ai/core";
10
- import { SdkError } from "@nyte-ai/schema";
10
+ import { asId, SdkError } from "@nyte-ai/schema";
11
+ var WAIT_MS = 60000;
11
12
  function normalizeAnswers(items, supplied) {
12
13
  if (supplied.length !== items.length)
13
14
  throw new SdkError("invalid", "Submit one answer list for each question.");
@@ -22,18 +23,58 @@ function normalizeAnswers(items, supplied) {
22
23
  return item.multiple ? selected.toSorted() : selected;
23
24
  });
24
25
  }
26
+ function formatAnswers(items, answers, form) {
27
+ return items.map((item, index) => `${form === "prompt" ? item.question : item.header}: ${answers[index].join(", ") || "Unanswered"}`).join(form === "prompt" ? `
28
+ ` : " · ");
29
+ }
25
30
  var handlers = {
26
31
  async answer(input, context) {
27
- let accepted = false;
32
+ let answered;
28
33
  const outcome = await context.part.update((current) => {
29
34
  const normalized = normalizeAnswers(current.questions, input.answers);
30
- accepted = current.answers === null || JSON.stringify(current.answers) === JSON.stringify(normalized);
31
- return current.answers === null ? { ...current, answers: normalized } : current;
35
+ const phase = current.phase;
36
+ if (phase.kind === "skipped")
37
+ throw new SdkError("invalid", "These questions were skipped.");
38
+ if (phase.kind === "answered") {
39
+ if (JSON.stringify(phase.answers) !== JSON.stringify(normalized))
40
+ throw new SdkError("invalid", "These questions were already answered.");
41
+ answered = { asked: current.questions, phase };
42
+ return current;
43
+ }
44
+ const next = {
45
+ kind: "answered",
46
+ answers: normalized,
47
+ head: phase.kind === "open" ? phase.head : null
48
+ };
49
+ answered = { asked: current.questions, phase: next };
50
+ return { ...current, phase: next };
32
51
  });
33
52
  if (outcome.kind !== "updated")
34
53
  throw new Error(`The questions could not be answered: ${outcome.kind}.`);
35
- if (!accepted)
36
- throw new SdkError("invalid", "These questions were already answered.");
54
+ if (answered?.phase.kind !== "answered" || answered.phase.head === null)
55
+ return;
56
+ await context.api.sdk.session(context.session).head(answered.phase.head).send(`The user answered your earlier question:
57
+ ${formatAnswers(answered.asked, answered.phase.answers, "prompt")}`, {
58
+ delivery: "attach",
59
+ source: {
60
+ kind: "answer",
61
+ part: context.part.id,
62
+ label: formatAnswers(answered.asked, answered.phase.answers, "label")
63
+ },
64
+ id: asId(`question:${context.part.id}`)
65
+ });
66
+ },
67
+ async skip(_, context) {
68
+ const outcome = await context.part.update((current) => {
69
+ const phase = current.phase;
70
+ if (phase.kind === "answered")
71
+ throw new SdkError("invalid", "These questions were already answered.");
72
+ if (phase.kind === "skipped")
73
+ return current;
74
+ return { ...current, phase: { kind: "skipped", late: phase.kind === "open" } };
75
+ });
76
+ if (outcome.kind !== "updated")
77
+ throw new Error(`The questions could not be skipped: ${outcome.kind}.`);
37
78
  }
38
79
  };
39
80
  var question = definePart({
@@ -41,10 +82,9 @@ var question = definePart({
41
82
  onAction: (action, input, context) => handlers[action](input, context)
42
83
  });
43
84
  var ask = {
44
- name: "question",
45
- description: "Ask the user one or more questions and wait for their answers. Give each option a short label and a description. Use multiple when several choices may apply. The user can also write their own answer.",
85
+ name: QUESTION_TOOL2,
86
+ description: "Ask the user one or more questions and wait up to a minute for their answers. Give each option a short label and a description. Use multiple when several choices may apply. The user can also write their own answer. If they don't answer in time, keep working; their answer arrives later as a message.",
46
87
  parameters: questionParameters2,
47
- output: questionOutput2,
48
88
  class: (args) => ({
49
89
  kind: "custom",
50
90
  label: args.questions.map((item) => item.header).join(", ")
@@ -58,26 +98,38 @@ var ask = {
58
98
  throw new SdkError("invalid", "Question option labels must be distinct.");
59
99
  }
60
100
  const part = await invocation.session.parts.create(question, {
61
- state: { ...args, answers: null },
101
+ state: { call: invocation.call, questions: args.questions, phase: { kind: "asking" } },
62
102
  placement: { kind: "modal" }
63
103
  });
64
104
  dev("question", "asked", { call: invocation.call, part: part.id });
65
- const outcome = await invocation.wait({ kind: "part", part: part.id });
105
+ const outcome = await invocation.wait({
106
+ kind: "part",
107
+ part: part.id,
108
+ deadline: invocation.now() + WAIT_MS
109
+ });
66
110
  const checked = outcome.kind === "part" && outcome.answer?.action === "answer" ? await answerInput2["~standard"].validate(outcome.answer.input) : undefined;
67
- const reply = checked !== undefined && checked.issues === undefined ? normalizeAnswers(args.questions, checked.value.answers) : null;
111
+ const deadline = outcome.kind === "deadline" ? await stopWaiting(part, invocation.head) : null;
112
+ const reply = deadline?.kind === "answered" && deadline.head === null ? deadline.answers : checked !== undefined && checked.issues === undefined ? normalizeAnswers(args.questions, checked.value.answers) : null;
113
+ const unanswered = deadline?.kind === "open" ? "No answer yet. The question stays open, and the user's answer will arrive as a message." : "The user did not answer.";
68
114
  return {
69
115
  content: [
70
116
  {
71
117
  kind: "text",
72
- text: reply === null ? "The user did not answer. Continue with your best judgment and say what you assumed." : args.questions.map((item, index) => `${item.question}: ${reply[index].join(", ") || "Unanswered"}`).join(`
73
- `)
118
+ text: reply === null ? `${unanswered} Continue with your best judgment and say what you assumed.` : formatAnswers(args.questions, reply, "prompt")
74
119
  }
75
120
  ],
76
- output: { answers: reply },
77
121
  parts: [part.id]
78
122
  };
79
123
  }
80
124
  };
125
+ async function stopWaiting(part, head) {
126
+ let phase = { kind: "open", head };
127
+ await part.update((current) => {
128
+ phase = current.phase.kind === "asking" ? { kind: "open", head } : current.phase;
129
+ return { ...current, phase };
130
+ });
131
+ return phase;
132
+ }
81
133
  var question_default = {
82
134
  id: "question",
83
135
  contribute: { parts: [question], tools: [ask] }
@@ -1,4 +1,6 @@
1
- import type { Infer, PartId, SessionView } from "@nyte-ai/schema";
1
+ import type { CallId, Infer, PartId, PartView, SessionView } from "@nyte-ai/schema";
2
+ /** The question tool's name, which clients match to draw its calls. */
3
+ export declare const QUESTION_TOOL = "question";
2
4
  declare const info: import("@nyte-ai/schema").Schema<{
3
5
  question: string;
4
6
  header: string;
@@ -11,10 +13,8 @@ declare const info: import("@nyte-ai/schema").Schema<{
11
13
  export declare const answerInput: import("@nyte-ai/schema").Schema<{
12
14
  answers: string[][];
13
15
  }>;
14
- export declare const questionOutput: import("@nyte-ai/schema").Schema<{
15
- answers: string[][] | null;
16
- }>;
17
16
  declare const state: import("@nyte-ai/schema").Schema<{
17
+ call: import("@nyte-ai/schema").Id<"call">;
18
18
  questions: {
19
19
  question: string;
20
20
  header: string;
@@ -24,7 +24,19 @@ declare const state: import("@nyte-ai/schema").Schema<{
24
24
  }[];
25
25
  multiple?: boolean | undefined;
26
26
  }[];
27
- answers: string[][] | null;
27
+ phase: {
28
+ kind: "asking";
29
+ } | {
30
+ kind: "open";
31
+ head: import("@nyte-ai/schema").Id<"head">;
32
+ } | {
33
+ kind: "answered";
34
+ answers: string[][];
35
+ head: import("@nyte-ai/schema").Id<"head"> | null;
36
+ } | {
37
+ kind: "skipped";
38
+ late: boolean;
39
+ };
28
40
  }>;
29
41
  export declare const questionParameters: import("@nyte-ai/schema").Schema<{
30
42
  questions: {
@@ -41,11 +53,18 @@ export declare const questionParameters: import("@nyte-ai/schema").Schema<{
41
53
  export type QuestionInfo = Infer<typeof info>;
42
54
  /** One answer list per question, in request order. An empty list leaves a question unanswered. */
43
55
  export type QuestionAnswers = Infer<typeof answerInput>["answers"];
44
- /** A grouped question request and its first complete answer, null while waiting. */
56
+ /**
57
+ * A grouped question request: the call that asked it, its questions, and where it stands.
58
+ * `asking` while the call waits; `open` once the call stopped waiting and an answer goes to `head`
59
+ * as a message. `answered` keeps that `head`, or null when the waiting call took the answer;
60
+ * `skipped` says whether the user declined after the call stopped waiting.
61
+ * A run that ends first shows on the part's status, not here.
62
+ */
45
63
  export type QuestionState = Infer<typeof state>;
64
+ export type QuestionPhase = QuestionState["phase"];
46
65
  /**
47
- * A grouped modal request. Its answer action submits all questions at once,
48
- * preserving the first complete answer across retries and restarts.
66
+ * A grouped modal request. `answer` submits all questions at once and `skip` declines them; the
67
+ * first of either holds across retries and restarts.
49
68
  *
50
69
  * @category plugins
51
70
  * @since 0.1.0
@@ -53,6 +72,7 @@ export type QuestionState = Infer<typeof state>;
53
72
  export declare const question: {
54
73
  kind: string;
55
74
  state: import("@nyte-ai/schema").Schema<{
75
+ call: import("@nyte-ai/schema").Id<"call">;
56
76
  questions: {
57
77
  question: string;
58
78
  header: string;
@@ -62,7 +82,19 @@ export declare const question: {
62
82
  }[];
63
83
  multiple?: boolean | undefined;
64
84
  }[];
65
- answers: string[][] | null;
85
+ phase: {
86
+ kind: "asking";
87
+ } | {
88
+ kind: "open";
89
+ head: import("@nyte-ai/schema").Id<"head">;
90
+ } | {
91
+ kind: "answered";
92
+ answers: string[][];
93
+ head: import("@nyte-ai/schema").Id<"head"> | null;
94
+ } | {
95
+ kind: "skipped";
96
+ late: boolean;
97
+ };
66
98
  }>;
67
99
  actions: {
68
100
  answer: {
@@ -70,8 +102,12 @@ export declare const question: {
70
102
  answers: string[][];
71
103
  }>;
72
104
  };
105
+ skip: {
106
+ input: import("@nyte-ai/schema").Schema<object>;
107
+ };
73
108
  };
74
109
  fallback: (current: {
110
+ call: import("@nyte-ai/schema").Id<"call">;
75
111
  questions: {
76
112
  question: string;
77
113
  header: string;
@@ -81,14 +117,27 @@ export declare const question: {
81
117
  }[];
82
118
  multiple?: boolean | undefined;
83
119
  }[];
84
- answers: string[][] | null;
120
+ phase: {
121
+ kind: "asking";
122
+ } | {
123
+ kind: "open";
124
+ head: import("@nyte-ai/schema").Id<"head">;
125
+ } | {
126
+ kind: "answered";
127
+ answers: string[][];
128
+ head: import("@nyte-ai/schema").Id<"head"> | null;
129
+ } | {
130
+ kind: "skipped";
131
+ late: boolean;
132
+ };
85
133
  }, { id }: {
86
134
  readonly id: PartId;
87
135
  }) => string;
88
136
  };
89
- /** A grouped request still waiting for the user, with the part its submission goes to. */
137
+ /** A grouped request still waiting for the user, with the part its submission goes to. `late` once the asking call stopped waiting and the agent carried on. */
90
138
  export type OpenQuestion = Pick<QuestionState, "questions"> & {
91
139
  readonly part: PartId;
140
+ readonly late: boolean;
92
141
  };
93
142
  /**
94
143
  * Returns unanswered live question requests, oldest first. Requests from a
@@ -98,4 +147,17 @@ export type OpenQuestion = Pick<QuestionState, "questions"> & {
98
147
  * @since 0.1.0
99
148
  */
100
149
  export declare function openQuestions(view: Pick<SessionView, "parts"> | undefined): readonly OpenQuestion[];
150
+ /**
151
+ * The question part a call made, found by its `call` with one string comparison per part; the
152
+ * caller validates only the match with {@link readQuestion}.
153
+ *
154
+ * @category plugins
155
+ */
156
+ export declare function findQuestionPart(parts: readonly PartView[], call: CallId): PartView | undefined;
157
+ /**
158
+ * A question part's state, or null when it doesn't parse.
159
+ *
160
+ * @category plugins
161
+ */
162
+ export declare function readQuestion(part: PartView): QuestionState | null;
101
163
  export {};
@@ -1,14 +1,18 @@
1
1
  import {
2
+ QUESTION_TOOL2,
2
3
  answerInput2,
3
- questionOutput2,
4
4
  questionParameters2,
5
5
  question2,
6
- openQuestions2
7
- } from "../../chunks/bash-description-bkhr9zrz.js";
6
+ openQuestions2,
7
+ findQuestionPart2,
8
+ readQuestion2
9
+ } from "../../chunks/bash-description-s6v2m3ch.js";
8
10
  export {
11
+ QUESTION_TOOL2 as QUESTION_TOOL,
9
12
  answerInput2 as answerInput,
13
+ findQuestionPart2 as findQuestionPart,
10
14
  openQuestions2 as openQuestions,
11
15
  question2 as question,
12
- questionOutput2 as questionOutput,
13
- questionParameters2 as questionParameters
16
+ questionParameters2 as questionParameters,
17
+ readQuestion2 as readQuestion
14
18
  };
@@ -20,7 +20,7 @@ declare const _default: {
20
20
  readonly message: import("@nyte-ai/schema").Id<"message">;
21
21
  } | {
22
22
  readonly kind: "continuation";
23
- readonly from: "child" | "job" | "wake";
23
+ readonly from: "answer" | "child" | "job" | "wake";
24
24
  };
25
25
  }>, { api }: {
26
26
  readonly api: {