toolroll 0.9.83 → 0.9.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 (137) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +94 -1192
  3. package/dist/agent-fence.d.ts +3 -3
  4. package/dist/agent-fence.js +3 -3
  5. package/dist/agent-onboard.d.ts +2 -0
  6. package/dist/agent-onboard.js +3 -0
  7. package/dist/assignment-ui.js +5 -1
  8. package/dist/browser/workspace.css +2 -2
  9. package/dist/browser/workspace.js +31 -31
  10. package/dist/browser-crew.js +5 -1
  11. package/dist/browser-workspace.d.ts +12 -0
  12. package/dist/chat-channel.d.ts +10 -1
  13. package/dist/chat-channel.js +35 -5
  14. package/dist/chat-decide.d.ts +16 -4
  15. package/dist/chat-decide.js +239 -31
  16. package/dist/chat-messages.d.ts +5 -0
  17. package/dist/chat-messages.js +1 -1
  18. package/dist/chat-quiet.js +3 -2
  19. package/dist/chat-review.d.ts +4 -0
  20. package/dist/chat-review.js +13 -4
  21. package/dist/chat-terms.d.ts +9 -0
  22. package/dist/chat-terms.js +46 -0
  23. package/dist/contracts/console-api.d.ts +4 -2
  24. package/dist/contracts/console-api.js +7 -2
  25. package/dist/contracts/lead-tools.d.ts +30 -0
  26. package/dist/contracts/lead-tools.js +3 -0
  27. package/dist/contracts/youiyoux-request.d.ts +248 -0
  28. package/dist/contracts/youiyoux-request.js +152 -0
  29. package/dist/control-setup.d.ts +9 -0
  30. package/dist/control-setup.js +31 -4
  31. package/dist/control-ui.d.ts +7 -1
  32. package/dist/control-ui.js +3 -2
  33. package/dist/desktop-update.d.ts +1 -1
  34. package/dist/dispatch.d.ts +15 -0
  35. package/dist/dispatch.js +26 -4
  36. package/dist/first-run.d.ts +5 -2
  37. package/dist/first-run.js +13 -2
  38. package/dist/flow-cadence.d.ts +8 -2
  39. package/dist/flow-cadence.js +16 -3
  40. package/dist/flow-card-task.d.ts +9 -0
  41. package/dist/flow-card-task.js +15 -6
  42. package/dist/folder-look.d.ts +3 -0
  43. package/dist/folder-look.js +8 -4
  44. package/dist/guides.js +4 -3
  45. package/dist/integrations-ui.js +9 -4
  46. package/dist/integrations.d.ts +9 -1
  47. package/dist/integrations.js +43 -2
  48. package/dist/landed-cleanup.d.ts +4 -0
  49. package/dist/landed-cleanup.js +3 -2
  50. package/dist/lead-capabilities.js +2 -1
  51. package/dist/lead-cli.js +3 -1
  52. package/dist/lead-tools.d.ts +2 -0
  53. package/dist/lead-tools.js +30 -4
  54. package/dist/lead.d.ts +2 -0
  55. package/dist/lead.js +3 -3
  56. package/dist/model-catalog.d.ts +5 -0
  57. package/dist/model-catalog.js +11 -2
  58. package/dist/model-choices.d.ts +188 -0
  59. package/dist/model-choices.js +405 -0
  60. package/dist/model-names.d.ts +6 -0
  61. package/dist/model-names.js +34 -0
  62. package/dist/models-ui.d.ts +69 -5
  63. package/dist/models-ui.js +53 -10
  64. package/dist/needs-you.d.ts +8 -0
  65. package/dist/needs-you.js +8 -0
  66. package/dist/operate.d.ts +22 -2
  67. package/dist/operate.js +107 -41
  68. package/dist/project-activity.d.ts +20 -0
  69. package/dist/project-activity.js +23 -0
  70. package/dist/project-admission.d.ts +4 -1
  71. package/dist/project-admission.js +23 -2
  72. package/dist/project-cli.d.ts +1 -0
  73. package/dist/project-cli.js +36 -19
  74. package/dist/project-identity.d.ts +9 -2
  75. package/dist/project-identity.js +80 -33
  76. package/dist/project-knowledge.d.ts +9 -9
  77. package/dist/project-outcomes.d.ts +119 -0
  78. package/dist/project-outcomes.js +79 -0
  79. package/dist/result-actions.d.ts +4 -0
  80. package/dist/result-actions.js +41 -9
  81. package/dist/result-review.d.ts +9 -0
  82. package/dist/result-review.js +45 -5
  83. package/dist/serve.js +60 -13
  84. package/dist/server/chat.js +5 -4
  85. package/dist/server/chrome.d.ts +3 -0
  86. package/dist/server/chrome.js +5 -2
  87. package/dist/server/console.css +17 -2
  88. package/dist/server/flows.js +4 -0
  89. package/dist/server/http.d.ts +1 -0
  90. package/dist/server/http.js +6 -0
  91. package/dist/server/pages.js +53 -7
  92. package/dist/server/people-tokens.js +40 -20
  93. package/dist/server/render-chat.js +3 -2
  94. package/dist/server/render-pages.d.ts +12 -2
  95. package/dist/server/render-pages.js +36 -16
  96. package/dist/server/render-people.d.ts +14 -2
  97. package/dist/server/render-people.js +35 -12
  98. package/dist/server/render-results.d.ts +2 -0
  99. package/dist/server/render-results.js +20 -7
  100. package/dist/server/render-settings.js +1 -0
  101. package/dist/server/render-tasks.d.ts +1 -0
  102. package/dist/server/render-tasks.js +22 -19
  103. package/dist/server/request-context.d.ts +2 -0
  104. package/dist/server/route-table.js +9 -1
  105. package/dist/server/settings.d.ts +58 -1
  106. package/dist/server/settings.js +216 -70
  107. package/dist/server/signup-gate.d.ts +9 -0
  108. package/dist/server/signup-gate.js +40 -0
  109. package/dist/server/task-views.js +6 -0
  110. package/dist/server/tasks.js +54 -8
  111. package/dist/settings-kit-script.d.ts +21 -0
  112. package/dist/settings-kit-script.js +67 -0
  113. package/dist/settings-kit.d.ts +137 -0
  114. package/dist/settings-kit.js +186 -0
  115. package/dist/settings-proofs.d.ts +64 -0
  116. package/dist/settings-proofs.js +101 -0
  117. package/dist/setup-guide.js +4 -2
  118. package/dist/spend-report.d.ts +87 -0
  119. package/dist/spend-report.js +265 -0
  120. package/dist/spend-ui.d.ts +29 -8
  121. package/dist/spend-ui.js +74 -39
  122. package/dist/store.d.ts +46 -3
  123. package/dist/store.js +165 -6
  124. package/dist/task-sizing.d.ts +5 -3
  125. package/dist/task-sizing.js +4 -4
  126. package/dist/task-source.d.ts +13 -1
  127. package/dist/task-source.js +17 -1
  128. package/dist/telegram-lead.js +7 -5
  129. package/dist/telegram.js +1 -1
  130. package/dist/toolroll-update.js +2 -2
  131. package/dist/webhooks.d.ts +6 -0
  132. package/dist/webhooks.js +25 -0
  133. package/dist/work-index.d.ts +4 -0
  134. package/dist/work-index.js +32 -0
  135. package/dist/youiyoux-intake.d.ts +234 -0
  136. package/dist/youiyoux-intake.js +908 -0
  137. package/package.json +11 -8
@@ -267,6 +267,11 @@ export declare class ChatMessages extends ChatCore {
267
267
  taskRef: number | null;
268
268
  run: number | null;
269
269
  project: string | null;
270
+ image?: {
271
+ run: number;
272
+ artifact: number;
273
+ sha256: string;
274
+ };
270
275
  }[];
271
276
  createProposalAction(action: {
272
277
  token: string;
@@ -609,7 +609,7 @@ export class ChatMessages extends ChatCore {
609
609
  const images = parts.filter(one => one.part.kind === "image").map(({ part }) => {
610
610
  const taskId = part.taskId;
611
611
  const ref = this.store.lookupRef(taskId);
612
- return { taskId, taskRef: ref?.id ?? null, run: part.run, project: ref?.repo ?? null };
612
+ return { taskId, taskRef: ref?.id ?? null, run: part.run, project: ref?.repo ?? null, ...(part.run === null || part.artifact === null || part.sha256 === null ? {} : { image: { run: part.run, artifact: part.artifact, sha256: part.sha256 } }) };
613
613
  });
614
614
  // The lead's own messages: a reply from a turn that was about one task,
615
615
  // and a card, which names its task (or, once it filed one, the new task).
@@ -13,6 +13,7 @@ import { phoneText, projectLabel } from "./telegram-status.js";
13
13
  import { isLifecycleNotification } from "./store.js";
14
14
  import { leadSubjectOf } from "./lead-voice.js";
15
15
  import { decideFallbackLink, decideTargetOf } from "./chat-decide.js";
16
+ import { YOUIYOUX_CARD_KIND } from "./contracts/youiyoux-request.js";
16
17
  /** What always makes a new message, even in quiet mode: a question or approval waiting, a failure or other
17
18
  * attention fact, a result Ready for review, a security alert or release, and anything addressed to one person
18
19
  * (a sign-in or plan-limit pause, an evening digest). */
@@ -104,8 +105,8 @@ export function quietCardView(store, taskRefs, now, root, viewer) {
104
105
  return { text, entities: [{ type: "bold", offset: 0, length: heading.length }], link: { label: "Open tasks", path: "/work" } };
105
106
  }
106
107
  /** Facts that carry their own controls or must stand alone (a security alert, a decision, a flow card, a
107
- * screenshot, an acceptance packet) are never folded into a batch. */
108
- const OWN_MESSAGE_KINDS = new Set(["security-release", "secret-detected", "flow-decision", "flow-card", "acceptance-ready", "acceptance-evidence"]);
108
+ * screenshot, an acceptance packet, a youiyoux pass's card) are never folded into a batch. */
109
+ const OWN_MESSAGE_KINDS = new Set(["security-release", "secret-detected", "flow-decision", "flow-card", "acceptance-ready", "acceptance-evidence", YOUIYOUX_CARD_KIND]);
109
110
  /** Whether this update for a person joins their two-minute batch: anything about one task that needs them,
110
111
  * except a fact that must stand alone. */
111
112
  export function joinsBatch(row) {
@@ -1,5 +1,6 @@
1
1
  import type { Store } from "./store.js";
2
2
  import type { VerifiedApprover } from "./principal.js";
3
+ import { type FeedbackImage } from "./result-review.js";
3
4
  export type ReviewSnapshot = {
4
5
  task: string;
5
6
  root: string;
@@ -9,11 +10,13 @@ export type ReviewSnapshot = {
9
10
  sha: string;
10
11
  source: string;
11
12
  title: string;
13
+ images?: FeedbackImage[];
12
14
  notes: {
13
15
  id: number;
14
16
  note: string;
15
17
  path: string | null;
16
18
  line: number | null;
19
+ image?: FeedbackImage | null;
17
20
  }[];
18
21
  };
19
22
  type Problem = {
@@ -32,6 +35,7 @@ export type ReviewRequest = {
32
35
  path: string | null;
33
36
  line: number | null;
34
37
  notes: number[];
38
+ image?: FeedbackImage | null;
35
39
  };
36
40
  export declare function reviewInputProblem(note: string | null, path: string | null, line: number | null): string | null;
37
41
  /** Chat and the result form write the same comments and call the same revision service.
@@ -1,7 +1,8 @@
1
+ import { selectResultImages } from "./chat-evidence.js";
1
2
  import { readVerifiedArtifact } from "./evidence.js";
2
3
  import { hasForbiddenControls, validateNote } from "./decision.js";
3
- import { isRevisionFeedback, revisionSourceOf } from "./result-review.js";
4
- import { createResultRevision } from "./result-actions.js";
4
+ import { parseFeedbackImage, isRevisionFeedback, revisionSourceOf } from "./result-review.js";
5
+ import { createResultRevision, feedbackImageProblem } from "./result-actions.js";
5
6
  /** Read precisely the result named, never silently substitute a newer version. */
6
7
  export function readChatResult(store, who, evidenceRoot, task, run) {
7
8
  const ref = store.lookupRef(task);
@@ -21,11 +22,13 @@ export function readChatResult(store, who, evidenceRoot, task, run) {
21
22
  const proven = readVerifiedArtifact(evidenceRoot, artifact);
22
23
  if (!proven.ok)
23
24
  return { ok: false, message: "The saved changes could not be verified. Restore the result before adding feedback." };
25
+ const images = selectResultImages(store, who, evidenceRoot, task, found.id);
24
26
  return { ok: true, snapshot: {
25
27
  task, root: family.root.id, execution: family.current.id, run: found.id,
26
28
  artifact: artifact.id, sha: artifact.sha256, source: revisionSourceOf(store.getScope(task)?.digest ?? null),
27
29
  title: store.getTask(task)?.title ?? task,
28
- notes: store.liveDiffComments(found.id).filter(isRevisionFeedback).map(one => ({ id: one.id, note: one.note, path: one.path, line: one.line })),
30
+ images: images.ok ? images.selection.images.map(one => ({ run: found.id, artifact: one.artifact, sha256: one.sha256 })) : [],
31
+ notes: store.liveDiffComments(found.id).filter(isRevisionFeedback).map(one => ({ id: one.id, note: one.note, path: one.path, line: one.line, image: one.image ?? null })),
29
32
  } };
30
33
  }
31
34
  export function reviewInputProblem(note, path, line) {
@@ -65,9 +68,15 @@ export function applyChatReview(store, who, evidenceRoot, request, now, allowMod
65
68
  return fail("Some feedback changed or was already used. Review it again.");
66
69
  if (operation === "note" && (note === null || notes.length > 0))
67
70
  return fail("A saved note needs feedback text and cannot consume other notes.");
71
+ const image = parseFeedbackImage(request.image);
72
+ if (image === false || (image !== null && note === null))
73
+ return fail("Choose a screenshot and write its feedback again.");
74
+ const imageProblem = feedbackImageProblem(store, evidenceRoot, live.run, image);
75
+ if (imageProblem !== null)
76
+ return fail(imageProblem);
68
77
  const batch = [...notes].sort((a, b) => a - b);
69
78
  if (note !== null) {
70
- const id = store.addDiffComment({ artifactId: live.artifact, runId: live.run, path, line, note, author: who.name }, now);
79
+ const id = store.addDiffComment({ artifactId: live.artifact, runId: live.run, path, line, note, author: who.name, image }, now);
71
80
  if (id === null)
72
81
  return fail("The note could not be saved.");
73
82
  batch.push(id);
@@ -0,0 +1,9 @@
1
+ import type { Scope } from "./scope.js";
2
+ /** What a plan's yes allows an agent to do, in a few plain words (the approval sheet's "You're allowing"). */
3
+ export declare function permissionPlainWords(profile: Scope["profile"] | null | undefined): string | null;
4
+ export type ShownScope = Pick<Scope, "goal" | "outOfScope" | "touches" | "acceptance" | "profile" | "budgetMicrousd">;
5
+ /** Every word of the terms a yes agrees to, as the card shows them: the goal, what's excluded, the files (or plainly any
6
+ * file), each requirement with the evidence it needs, and what the agent may do and spend. Nothing is shortened. */
7
+ export declare function scopeTermsText(scope: ShownScope): string;
8
+ /** The digest of the terms shown, bound to the scope's own digest: a changed word, limit or route is another digest. */
9
+ export declare function shownTermsDigest(scope: ShownScope & Pick<Scope, "digest">): string;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * A scope's terms as a chat card shows them whole, and the digest of exactly those words. A task filed from outside
3
+ * text (a youiyoux change request) approves from chat only on a card that showed every word of its terms, nothing cut:
4
+ * the card binds that digest to its button, and the seal (store.ts, the outside-request quarantine) recomputes it from
5
+ * the live scope and refuses a tap that names anything else. A leaf: the store and the chat road both read it.
6
+ */
7
+ import { createHash } from "node:crypto";
8
+ import { isAcpProfile } from "./acp-agents.js";
9
+ /** What a plan's yes allows an agent to do, in a few plain words (the approval sheet's "You're allowing"). */
10
+ export function permissionPlainWords(profile) {
11
+ if (profile === null || profile === undefined)
12
+ return null;
13
+ const full = "full access, nothing asks first";
14
+ if (profile.provider === "claude") {
15
+ return profile.permissionArgv === "bypassPermissions" ? full
16
+ : profile.permissionArgv === "auto" ? "file edits and routine commands; anything risky stops"
17
+ : "file edits only; commands are refused";
18
+ }
19
+ if (profile.provider === "gemini")
20
+ return profile.approvalArgv === "yolo" ? full : "file edits only; other tools are refused";
21
+ if (isAcpProfile(profile))
22
+ return "full access inside the OS fence, nothing asks first";
23
+ return profile.sandboxMode === "danger-full-access" ? full : "file edits and commands inside the project only";
24
+ }
25
+ const money = (micro) => `$${(micro / 1_000_000).toFixed(2)}`;
26
+ /** Every word of the terms a yes agrees to, as the card shows them: the goal, what's excluded, the files (or plainly any
27
+ * file), each requirement with the evidence it needs, and what the agent may do and spend. Nothing is shortened. */
28
+ export function scopeTermsText(scope) {
29
+ const allows = [permissionPlainWords(scope.profile) ?? "agent permissions not known", scope.budgetMicrousd === null ? "no attempt limit" : `up to ${money(scope.budgetMicrousd)} per attempt`];
30
+ return [
31
+ "Goal:", scope.goal,
32
+ "",
33
+ "Not this:", scope.outOfScope ?? "Nothing excluded.",
34
+ "",
35
+ `Files: ${scope.touches.length === 0 ? "any file (no file list)" : scope.touches.join(", ")}`,
36
+ "",
37
+ "Done when:",
38
+ ...(scope.acceptance.length === 0 ? ["You decide when you review the result."] : scope.acceptance.map(one => `• ${one.statement} (${one.evidence.join(", ")})`)),
39
+ "",
40
+ `Starting allows: ${allows.join(" · ")}`,
41
+ ].join("\n");
42
+ }
43
+ /** The digest of the terms shown, bound to the scope's own digest: a changed word, limit or route is another digest. */
44
+ export function shownTermsDigest(scope) {
45
+ return createHash("sha256").update(JSON.stringify({ v: 1, scope: scope.digest, shown: scopeTermsText(scope) })).digest("hex");
46
+ }
@@ -102,11 +102,13 @@ export declare const CONSOLE_FORMS: {
102
102
  readonly data: FormContract<"password">;
103
103
  readonly flowsOn: FormContract<"repo" | "starter">;
104
104
  readonly integrationsTest: FormContract<"key">;
105
+ readonly integrationsYouiyoux: FormContract<"on" | "repo">;
105
106
  readonly monitoring: FormContract<"folder" | "header-name" | "header-value" | "password" | "rotate" | "traces" | "webhook">;
106
107
  readonly toolsConnect: FormContract<"access" | "also" | "kit" | "password" | "repo" | "service" | "shown" | "template">;
107
108
  readonly toolsChange: FormContract<"action" | "catalog" | "import" | "name" | "password" | "repo" | "secret" | "secrets" | "shown" | "target" | "transport" | "value">;
108
109
  readonly appearance: FormContract<"accent" | "quiet" | "return" | "theme">;
109
- readonly models: FormContract<"agent" | "enabled" | "phase" | "tool">;
110
+ readonly models: FormContract<"enabled" | "tool">;
111
+ readonly modelsChange: FormContract<"build" | "build-light" | "build-strong" | "change" | "digest" | "password" | "plan" | "plan-strong" | "preset" | "repair" | "review" | "review-strong" | "scope" | "switch" | "to">;
110
112
  readonly knowledgeRefresh: FormContract<"repo">;
111
113
  readonly knowledgeProposal: FormContract<"decision" | "proposal" | "repo">;
112
114
  readonly knowledgeDecision: FormContract<"action" | "claim" | "decision" | "reason" | "repo" | "why">;
@@ -167,7 +169,7 @@ export declare const CONSOLE_FORMS: {
167
169
  readonly chatAck: FormContract<"nonce" | "token">;
168
170
  readonly recipes: FormContract<"document" | "next" | "preview" | "projectRevision" | "purpose" | "recipe" | "recipeRevision" | "repo" | "source" | "sourceRevision">;
169
171
  readonly runNote: FormContract<"note">;
170
- readonly diffComment: FormContract<"batch" | "intent" | "line" | "note" | "path" | "request" | "return" | "source" | "tab">;
172
+ readonly diffComment: FormContract<"batch" | "comment" | "image" | "intent" | "line" | "note" | "path" | "request" | "return" | "source" | "tab">;
171
173
  readonly editorLinks: FormContract<"on" | "return">;
172
174
  readonly revise: FormContract<"batch" | "return" | "source">;
173
175
  readonly followUp: FormContract<"level" | "return">;
@@ -111,11 +111,16 @@ export const CONSOLE_FORMS = {
111
111
  data: formContract("POST /settings/data", ["password"]),
112
112
  flowsOn: formContract("POST /settings/flows/on", ["repo", "starter"]),
113
113
  integrationsTest: formContract("POST /settings/integrations/test", ["key"]),
114
+ integrationsYouiyoux: formContract("POST /settings/integrations/youiyoux", ["repo", "on"]),
114
115
  monitoring: formContract("POST /settings/monitoring", ["password", "webhook", "folder", "traces", "header-name", "header-value", "rotate"]),
115
116
  toolsConnect: formContract("POST /settings/tools/connect", ["repo", "also", "service", "template", "kit", "shown", "password", "access"]),
116
117
  toolsChange: formContract("POST /settings/tools/change", ["repo", "shown", "action", "name", "password", "catalog", "transport", "target", "secrets", "import", "secret", "value"]),
117
118
  appearance: formContract("POST /settings/appearance", ["accent", "quiet", "return", "theme"]),
118
- models: formContract("POST /settings/models/check, /settings/models/watch, /settings/models/update, /settings/models/agent", ["enabled", "tool", "phase", "agent"]),
119
+ models: formContract("POST /settings/models/check, /settings/models/watch, /settings/models/update", ["enabled", "tool"]),
120
+ // The one Models editor (docs/plans/spend-dashboard.md slice 3): every role at a scope in one save, or a previewed
121
+ // preset or switch; the digest is what the page showed, and the change digest what the preview showed.
122
+ modelsChange: formContract("POST /settings/models/agent, /settings/models/apply", ["password", "scope", "digest", "change", "preset", "switch", "to",
123
+ "plan", "plan-strong", "build-light", "build", "build-strong", "repair", "review", "review-strong"]),
119
124
  knowledgeRefresh: formContract("POST /settings/knowledge/refresh", ["repo"]),
120
125
  knowledgeProposal: formContract("POST /settings/knowledge/proposal", ["repo", "decision", "proposal"]),
121
126
  knowledgeDecision: formContract("POST /settings/knowledge/decision", ["repo", "action", "claim", "why", "decision", "reason"]),
@@ -178,7 +183,7 @@ export const CONSOLE_FORMS = {
178
183
  // A recipe's own answers and editor fields are read by recipes.ts from the whole form (`sent`; item 17).
179
184
  recipes: formContract("POST /recipes/prepare, /recipes/preview, /recipes/import, /recipes/launch, /recipes/save", ["repo", "projectRevision", "recipe", "recipeRevision", "document", "purpose", "source", "sourceRevision", "preview", "next"]),
180
185
  runNote: formContract("POST /r/<run>/note", ["note"]),
181
- diffComment: formContract("POST /r/<run>/comment", ["return", "tab", "intent", "batch", "source", "note", "path", "line", "request"]),
186
+ diffComment: formContract("POST /r/<run>/comment", ["return", "tab", "intent", "batch", "source", "note", "path", "line", "request", "image", "comment"]),
182
187
  editorLinks: formContract("POST /session/editor-links", ["on", "return"]),
183
188
  revise: formContract("POST /r/<run>/revise", ["return", "batch", "source"]),
184
189
  followUp: formContract("POST /r/<run>/checks|add-tests", ["return", "level"]),
@@ -36,6 +36,10 @@ export declare const LEAD_TOOL_INPUTS: {
36
36
  readonly get_brief: z.ZodObject<{
37
37
  repo: z.ZodOptional<z.ZodString>;
38
38
  }, z.core.$strict>;
39
+ readonly get_project_activity: z.ZodObject<{
40
+ repo: z.ZodString;
41
+ offset: z.ZodOptional<z.ZodInt>;
42
+ }, z.core.$strict>;
39
43
  readonly get_project_context: z.ZodObject<{
40
44
  repo: z.ZodString;
41
45
  query: z.ZodString;
@@ -130,6 +134,7 @@ export declare const LEAD_TOOL_INPUTS: {
130
134
  path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
131
135
  line: z.ZodOptional<z.ZodNullable<z.ZodInt>>;
132
136
  saved_notes: z.ZodOptional<z.ZodArray<z.ZodInt>>;
137
+ image: z.ZodOptional<z.ZodNullable<z.ZodInt>>;
133
138
  }, z.core.$strict>;
134
139
  readonly recap: z.ZodObject<{
135
140
  since: z.ZodOptional<z.ZodString>;
@@ -762,6 +767,10 @@ export declare const LEAD_TOOL_READ_INPUTS: {
762
767
  readonly get_brief: z.ZodObject<{
763
768
  repo: z.ZodOptional<z.ZodString>;
764
769
  }, z.core.$strict>;
770
+ readonly get_project_activity: z.ZodObject<{
771
+ repo: z.ZodString;
772
+ offset: z.ZodOptional<z.ZodInt>;
773
+ }, z.core.$strict>;
765
774
  readonly get_project_context: z.ZodObject<{
766
775
  repo: z.ZodString;
767
776
  query: z.ZodString;
@@ -856,6 +865,7 @@ export declare const LEAD_TOOL_READ_INPUTS: {
856
865
  path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
857
866
  line: z.ZodOptional<z.ZodNullable<z.ZodInt>>;
858
867
  saved_notes: z.ZodOptional<z.ZodArray<z.ZodInt>>;
868
+ image: z.ZodOptional<z.ZodNullable<z.ZodInt>>;
859
869
  }, z.core.$strict>;
860
870
  readonly recap: z.ZodObject<{
861
871
  since: z.ZodOptional<z.ZodString>;
@@ -1556,6 +1566,26 @@ export declare const LEAD_TOOL_OUTPUTS: {
1556
1566
  projects: z.ZodArray<z.ZodUnknown>;
1557
1567
  omissions: z.ZodObject<{}, z.core.$loose>;
1558
1568
  }, z.core.$loose>;
1569
+ readonly get_project_activity: z.ZodObject<{
1570
+ asOf: z.ZodString;
1571
+ since: z.ZodString;
1572
+ days: z.ZodNumber;
1573
+ limit: z.ZodNumber;
1574
+ truncated: z.ZodBoolean;
1575
+ ready: z.ZodObject<{
1576
+ count: z.ZodNumber;
1577
+ oldestFinishedAgeMs: z.ZodNullable<z.ZodNumber>;
1578
+ dated: z.ZodNumber;
1579
+ }, z.core.$strip>;
1580
+ completed: z.ZodObject<{
1581
+ count: z.ZodNumber;
1582
+ }, z.core.$loose>;
1583
+ families: z.ZodArray<z.ZodUnknown>;
1584
+ familiesTotal: z.ZodNumber;
1585
+ familiesOffset: z.ZodNumber;
1586
+ nextOffset: z.ZodNullable<z.ZodNumber>;
1587
+ incompleteHistory: z.ZodNumber;
1588
+ }, z.core.$loose>;
1559
1589
  readonly get_project_context: z.ZodObject<{
1560
1590
  version: z.ZodLiteral<1>;
1561
1591
  readOnly: z.ZodBoolean;
@@ -52,6 +52,7 @@ export const LISTED_TASK_STATES = ["queued", "running", "done", "failed", "cance
52
52
  const ACTION_OPERATIONS = Object.keys(CHAT_ACTIONS).filter(one => !one.startsWith("flow_") && !one.startsWith("subagent_"));
53
53
  export const LEAD_TOOL_INPUTS = {
54
54
  get_brief: z.strictObject({ repo: repoId.optional() }),
55
+ get_project_activity: z.strictObject({ repo: repoId, offset: offset.optional() }),
55
56
  get_project_context: z.strictObject({ repo: repoId, query: text("contextQuery").min(1), mode: z.enum(["search", "impact"]).optional() }),
56
57
  get_action_status: z.strictObject({ proposal: id }),
57
58
  get_actions: z.strictObject({}),
@@ -77,6 +78,7 @@ export const LEAD_TOOL_INPUTS = {
77
78
  note: z.string().max(LIMITS.note).nullable().optional(), path: text("diffPath").nullable().optional(),
78
79
  line: z.int().min(1).max(1_000_000).nullable().optional(),
79
80
  saved_notes: z.array(id).max(100).optional(),
81
+ image: id.nullable().optional().describe("Screenshot artifact id from get_result_images; attaches the exact verified image to this feedback. null leaves off the screenshot a reply targets."),
80
82
  }),
81
83
  recap: z.strictObject({ since: text("recapSince").optional() }),
82
84
  list_repos: z.strictObject({}),
@@ -196,6 +198,7 @@ export const decisionOutput = z.looseObject({ repo: str, decision: int, task: st
196
198
  export const queueColumnsOutput = { columns: rows({ column: str, tasks: list }) };
197
199
  export const LEAD_TOOL_OUTPUTS = {
198
200
  get_brief: catchUpOutput,
201
+ get_project_activity: z.looseObject({ asOf: str, since: str, days: int, limit: int, truncated: z.boolean(), ready: z.object({ count: int, oldestFinishedAgeMs: nullable(int), dated: int }), completed: z.looseObject({ count: int }), families: list, familiesTotal: int, familiesOffset: int, nextOffset: nullable(int), incompleteHistory: int }),
199
202
  get_project_context: repositoryContextOutput,
200
203
  get_action_status: z.looseObject({ proposal: int, operation: str, state: str, outcome: z.unknown(), finishedAt: nullable(str) }),
201
204
  get_actions: z.looseObject({ actions: rows({ operation: str, label: str, secureReview: z.boolean(), inputs: z.array(str) }), notice: str }),
@@ -0,0 +1,248 @@
1
+ /**
2
+ * A youiyoux request as Toolroll reads it (youiyoux-intake.ts): the published `youi.request/v1` schema (youiyoux's
3
+ * schema/youi.request.schema.json), held to its own limits, and the `youi.proposal/v1` options file an options request's
4
+ * agent writes (schema/youi.proposal.schema.json). Unknown keys are allowed, as the published schemas allow them;
5
+ * everything Toolroll reads is checked here, and every string is outside text all the same.
6
+ */
7
+ import { z } from "zod";
8
+ import { type ContractResult } from "./contract.js";
9
+ export declare const REQUEST_ID: RegExp;
10
+ /** An options file's id: `p-` and the same 10 hex as the options request it answers. */
11
+ export declare const PROPOSAL_ID: RegExp;
12
+ /** The provenance every task filed from a request carries (task_ref.filed_via): immutable, and the seal reads it. */
13
+ export declare const YOUIYOUX_FILED_VIA = "youiyoux";
14
+ /** The one chat card a pass's filings make for a project (youiyoux-intake.ts), drawn by chat-decide.ts: the new tasks,
15
+ * Approve all and each one's own Approve and Open. */
16
+ export declare const YOUIYOUX_CARD_KIND = "youiyoux-requests";
17
+ /** Where a person sees and approves everything waiting on them in the console (flow-triggers.ts NEEDS_YOU_HREF). */
18
+ export declare const YOUIYOUX_HOME_PATH = "/work?view=needs-you";
19
+ export declare const YOUIYOUX_EVIDENCE: readonly ["screenshot", "check", "changed-path", "manual-review"];
20
+ /** What an options request offers its agent: the one file to write (`.youi/proposals/<proposal>.json`), the props an
21
+ * option may set (enum values from `values`), and whether it may give the component new text. */
22
+ export declare const youiyouxOfferSchema: z.ZodObject<{
23
+ proposal: z.ZodString;
24
+ file: z.ZodString;
25
+ choices: z.ZodRecord<z.ZodString, z.ZodObject<{
26
+ type: z.ZodEnum<{
27
+ boolean: "boolean";
28
+ enum: "enum";
29
+ number: "number";
30
+ string: "string";
31
+ }>;
32
+ values: z.ZodOptional<z.ZodArray<z.ZodString>>;
33
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
34
+ }, z.core.$strip>>;
35
+ text: z.ZodBoolean;
36
+ }, z.core.$strip>;
37
+ export type YouiyouxOffer = z.infer<typeof youiyouxOfferSchema>;
38
+ export declare const youiyouxTaskSchema: z.ZodObject<{
39
+ title: z.ZodString;
40
+ goal: z.ZodString;
41
+ touches: z.ZodArray<z.ZodString>;
42
+ touchesKnown: z.ZodBoolean;
43
+ why: z.ZodOptional<z.ZodString>;
44
+ not: z.ZodArray<z.ZodString>;
45
+ acceptance: z.ZodArray<z.ZodObject<{
46
+ statement: z.ZodString;
47
+ evidence: z.ZodArray<z.ZodEnum<{
48
+ "changed-path": "changed-path";
49
+ check: "check";
50
+ "manual-review": "manual-review";
51
+ screenshot: "screenshot";
52
+ }>>;
53
+ }, z.core.$strip>>;
54
+ offer: z.ZodOptional<z.ZodObject<{
55
+ proposal: z.ZodString;
56
+ file: z.ZodString;
57
+ choices: z.ZodRecord<z.ZodString, z.ZodObject<{
58
+ type: z.ZodEnum<{
59
+ boolean: "boolean";
60
+ enum: "enum";
61
+ number: "number";
62
+ string: "string";
63
+ }>;
64
+ values: z.ZodOptional<z.ZodArray<z.ZodString>>;
65
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
66
+ }, z.core.$strip>>;
67
+ text: z.ZodBoolean;
68
+ }, z.core.$strip>>;
69
+ }, z.core.$strip>;
70
+ export declare const youiyouxRequestSchema: z.ZodObject<{
71
+ schema: z.ZodLiteral<"youi.request/v1">;
72
+ id: z.ZodString;
73
+ project: z.ZodString;
74
+ kind: z.ZodEnum<{
75
+ change: "change";
76
+ comment: "comment";
77
+ options: "options";
78
+ }>;
79
+ status: z.ZodEnum<{
80
+ addressed: "addressed";
81
+ closed: "closed";
82
+ open: "open";
83
+ }>;
84
+ body: z.ZodString;
85
+ author: z.ZodObject<{
86
+ kind: z.ZodEnum<{
87
+ agent: "agent";
88
+ person: "person";
89
+ }>;
90
+ name: z.ZodString;
91
+ }, z.core.$strip>;
92
+ createdAt: z.ZodString;
93
+ updatedAt: z.ZodString;
94
+ target: z.ZodObject<{
95
+ route: z.ZodString;
96
+ scope: z.ZodEnum<{
97
+ element: "element";
98
+ here: "here";
99
+ }>;
100
+ name: z.ZodString;
101
+ file: z.ZodOptional<z.ZodString>;
102
+ line: z.ZodOptional<z.ZodInt>;
103
+ }, z.core.$strip>;
104
+ task: z.ZodOptional<z.ZodObject<{
105
+ title: z.ZodString;
106
+ goal: z.ZodString;
107
+ touches: z.ZodArray<z.ZodString>;
108
+ touchesKnown: z.ZodBoolean;
109
+ why: z.ZodOptional<z.ZodString>;
110
+ not: z.ZodArray<z.ZodString>;
111
+ acceptance: z.ZodArray<z.ZodObject<{
112
+ statement: z.ZodString;
113
+ evidence: z.ZodArray<z.ZodEnum<{
114
+ "changed-path": "changed-path";
115
+ check: "check";
116
+ "manual-review": "manual-review";
117
+ screenshot: "screenshot";
118
+ }>>;
119
+ }, z.core.$strip>>;
120
+ offer: z.ZodOptional<z.ZodObject<{
121
+ proposal: z.ZodString;
122
+ file: z.ZodString;
123
+ choices: z.ZodRecord<z.ZodString, z.ZodObject<{
124
+ type: z.ZodEnum<{
125
+ boolean: "boolean";
126
+ enum: "enum";
127
+ number: "number";
128
+ string: "string";
129
+ }>;
130
+ values: z.ZodOptional<z.ZodArray<z.ZodString>>;
131
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
132
+ }, z.core.$strip>>;
133
+ text: z.ZodBoolean;
134
+ }, z.core.$strip>>;
135
+ }, z.core.$strip>>;
136
+ thread: z.ZodArray<z.ZodUnknown>;
137
+ }, z.core.$strip>;
138
+ export type YouiyouxRequest = z.infer<typeof youiyouxRequestSchema>;
139
+ export type YouiyouxTask = z.infer<typeof youiyouxTaskSchema>;
140
+ export declare function readYouiyouxRequest(raw: unknown): ContractResult<YouiyouxRequest>;
141
+ /** The one file an options request's agent may write: `.youi/proposals/p-<the request's hex>.json`. */
142
+ export declare const proposalIdOf: (request: string) => string;
143
+ export declare const proposalFileOf: (proposal: string) => string;
144
+ /** Why an options request can't be filed as youiyoux's contract has it, or null: it carries a task with an offer whose
145
+ * proposal is `p-<its own hex>`, whose file is that proposal's, and the task may touch that one file and nothing else. */
146
+ export declare function optionsProblem(request: YouiyouxRequest): string | null;
147
+ export declare const PROPOSAL_STATUSES: readonly ["open", "chosen", "dismissed"];
148
+ export declare const youiyouxProposalSchema: z.ZodObject<{
149
+ $schema: z.ZodOptional<z.ZodString>;
150
+ schema: z.ZodLiteral<"youi.proposal/v1">;
151
+ id: z.ZodString;
152
+ project: z.ZodString;
153
+ route: z.ZodString;
154
+ target: z.ZodObject<{
155
+ name: z.ZodString;
156
+ element: z.ZodOptional<z.ZodString>;
157
+ file: z.ZodOptional<z.ZodString>;
158
+ line: z.ZodOptional<z.ZodInt>;
159
+ column: z.ZodOptional<z.ZodInt>;
160
+ box: z.ZodObject<{
161
+ x: z.ZodNumber;
162
+ y: z.ZodNumber;
163
+ width: z.ZodNumber;
164
+ height: z.ZodNumber;
165
+ }, z.core.$strip>;
166
+ page: z.ZodOptional<z.ZodObject<{
167
+ width: z.ZodNumber;
168
+ height: z.ZodNumber;
169
+ }, z.core.$strip>>;
170
+ }, z.core.$strip>;
171
+ ask: z.ZodString;
172
+ author: z.ZodObject<{
173
+ kind: z.ZodEnum<{
174
+ agent: "agent";
175
+ person: "person";
176
+ }>;
177
+ name: z.ZodString;
178
+ }, z.core.$strip>;
179
+ createdAt: z.ZodString;
180
+ updatedAt: z.ZodString;
181
+ options: z.ZodArray<z.ZodObject<{
182
+ id: z.ZodEnum<{
183
+ a: "a";
184
+ b: "b";
185
+ c: "c";
186
+ d: "d";
187
+ }>;
188
+ label: z.ZodString;
189
+ why: z.ZodString;
190
+ set: z.ZodArray<z.ZodObject<{
191
+ prop: z.ZodString;
192
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
193
+ }, z.core.$strip>>;
194
+ text: z.ZodNullable<z.ZodString>;
195
+ }, z.core.$strip>>;
196
+ status: z.ZodEnum<{
197
+ chosen: "chosen";
198
+ dismissed: "dismissed";
199
+ open: "open";
200
+ }>;
201
+ choice: z.ZodOptional<z.ZodEnum<{
202
+ a: "a";
203
+ b: "b";
204
+ c: "c";
205
+ d: "d";
206
+ }>>;
207
+ decidedBy: z.ZodOptional<z.ZodObject<{
208
+ kind: z.ZodEnum<{
209
+ agent: "agent";
210
+ person: "person";
211
+ }>;
212
+ name: z.ZodString;
213
+ }, z.core.$strip>>;
214
+ decidedAt: z.ZodOptional<z.ZodString>;
215
+ request: z.ZodOptional<z.ZodString>;
216
+ for: z.ZodString;
217
+ }, z.core.$strip>;
218
+ export type YouiyouxProposal = z.infer<typeof youiyouxProposalSchema>;
219
+ export declare function readYouiyouxProposal(raw: unknown): ContractResult<YouiyouxProposal>;
220
+ /** What Toolroll writes beside a request (`youi.request-task/v1`): the task it became, how that task stands, and, when
221
+ * the console has an address, the task's link and `home`, where a person sees and approves all waiting work. */
222
+ export declare const YOUIYOUX_TASK_STATUSES: readonly ["queued", "needs-you", "building", "ready", "done", "failed", "stopped"];
223
+ export type YouiyouxTaskStatus = (typeof YOUIYOUX_TASK_STATUSES)[number];
224
+ /** A link youiyoux opens: http or https, at most 2,000 characters, with no user name or password in it. */
225
+ export declare const youiyouxLinkSchema: z.ZodString;
226
+ export declare const youiyouxRequestTaskSchema: z.ZodObject<{
227
+ schema: z.ZodLiteral<"youi.request-task/v1">;
228
+ request: z.ZodString;
229
+ runner: z.ZodLiteral<"Toolroll">;
230
+ task: z.ZodObject<{
231
+ id: z.ZodString;
232
+ url: z.ZodNullable<z.ZodString>;
233
+ }, z.core.$strict>;
234
+ home: z.ZodOptional<z.ZodString>;
235
+ status: z.ZodEnum<{
236
+ building: "building";
237
+ done: "done";
238
+ failed: "failed";
239
+ "needs-you": "needs-you";
240
+ queued: "queued";
241
+ ready: "ready";
242
+ stopped: "stopped";
243
+ }>;
244
+ detail: z.ZodString;
245
+ updatedAt: z.ZodString;
246
+ }, z.core.$strict>;
247
+ export type YouiyouxRequestTask = z.infer<typeof youiyouxRequestTaskSchema>;
248
+ export declare function readYouiyouxRequestTask(raw: unknown): ContractResult<YouiyouxRequestTask>;