@piagent/platform 1.9.2 → 1.10.0
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.
- package/CHANGELOG.md +45 -0
- package/README.md +1 -1
- package/README.vi.md +1 -1
- package/docs/command-reference-vietnamese.md +2 -2
- package/docs/distribution-standard.md +6 -6
- package/docs/en/architecture.md +1 -1
- package/docs/languages.json +4 -4
- package/docs/managed-local-acceptance.md +35 -0
- package/docs/operator-manual-vietnamese.md +7 -7
- package/docs/publishing-for-teams.md +2 -0
- package/docs/release-install-policy.md +2 -2
- package/docs/team-onboarding.md +2 -2
- package/docs/vi/architecture.md +1 -1
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +1 -1
- package/docs-site/commands.html +1 -1
- package/docs-site/content/en/index.html +4 -4
- package/docs-site/content/en/whats-new.html +22 -0
- package/docs-site/content/index.html +4 -4
- package/docs-site/content/whats-new.html +22 -0
- package/docs-site/context-engine.html +1 -1
- package/docs-site/ecosystem.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +1 -1
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/ecosystem.html +1 -1
- package/docs-site/en/index.html +5 -5
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +1 -1
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +23 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +5 -5
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +1 -1
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +23 -1
- package/docs-site/workflows.html +1 -1
- package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
- package/package.json +1 -1
- package/packages/piagent-core/managed/grant-share.mjs +18 -0
- package/packages/piagent-core/managed/helper-roles.mjs +8 -4
- package/packages/piagent-core/managed/helper-run.mjs +116 -0
- package/packages/piagent-core/managed/member-questions.d.mts +15 -0
- package/packages/piagent-core/managed/member-questions.mjs +178 -0
- package/packages/piagent-core/managed/objections.mjs +73 -0
- package/packages/piagent-core/managed/patch-snapshot.mjs +108 -22
- package/packages/piagent-core/managed/request-stream.mjs +54 -7
- package/packages/piagent-core/managed/review-snapshot-worker.mjs +31 -13
- package/packages/piagent-core/managed/session.mjs +37 -89
- package/packages/piagent-core/managed/web-fetch.mjs +5 -0
- package/packages/piagent-core/managed/web-search.mjs +23 -2
- package/packages/piagent-core/managed/workflow.mjs +137 -51
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/runtime/managed-failure.mjs +6 -2
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-BAeuN-Z9.js → MarkdownMessage-CnSRvuah.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-DgSmcnP8.css → index-Z91z6SlN.css} +1 -1
- package/packages/piagent-webui/dist/client/assets/{index-ClXiZwIU.js → index-yg9s8sA_.js} +14 -14
- package/packages/piagent-webui/dist/client/index.html +2 -2
- package/packages/piagent-webui/gateway/gateway-service.ts +7 -0
- package/packages/piagent-webui/gateway/managed-projection.ts +1 -1
- package/packages/piagent-webui/gateway/session-questions.ts +15 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +7 -2
- package/packages/piagent-webui/server/loopback-server.ts +29 -0
- package/packages/piagent-webui/server/transcript-projection.ts +17 -4
- package/schemas/piagent-webui/session-catalog-v1.schema.json +1 -1
- package/schemas/piagent-webui/transcript-v1.schema.json +29 -2
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<meta name="csp-nonce" content="__PIAGENT_CSP_NONCE__" />
|
|
8
8
|
<meta name="piagent-webui-mode" content="__PIAGENT_WEBUI_MODE__" />
|
|
9
9
|
<title>Piagent WebUI</title>
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-yg9s8sA_.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Z91z6SlN.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="root"></div>
|
|
@@ -276,6 +276,13 @@ export async function startPiagentGateway(options: {
|
|
|
276
276
|
? relayPost("/api/v1/runtime-commands", command) : runtimeCommands.execute(command),
|
|
277
277
|
executeSessionAttachment: (sessionRef, command) => company(sessionRef)
|
|
278
278
|
? relayPost(`/api/v1/sessions/${encodeURIComponent(sessionRef)}/attachments`, command) : attachments.execute(sessionRef, command),
|
|
279
|
+
// The main agent's questions to the member, read and answered where the
|
|
280
|
+
// conversation runs (the company Gateway for a company conversation).
|
|
281
|
+
readSessionQuestions: (sessionRef) => company(sessionRef)
|
|
282
|
+
? relay!.json("GET", `/api/v1/sessions/${encodeURIComponent(sessionRef)}/questions`) : runtimes!.questions(sessionRef),
|
|
283
|
+
answerSessionQuestion: (sessionRef, questionRef, answer) => company(sessionRef)
|
|
284
|
+
? relayPost(`/api/v1/sessions/${encodeURIComponent(sessionRef)}/questions/${encodeURIComponent(questionRef)}/answer`, answer)
|
|
285
|
+
: runtimes!.answerQuestion(sessionRef, questionRef, answer),
|
|
279
286
|
// Approval refs carry no session; a company approval is not pending here.
|
|
280
287
|
executeApproval: async (approvalRef, decision) => {
|
|
281
288
|
try { return await runtimes!.decideApproval(approvalRef, decision); }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Public managed state contains no provider/model route or credential.
|
|
2
2
|
const STATUS = ['pending', 'in_progress', 'completed'];
|
|
3
|
-
const OUTCOMES = ['no_change', 'interrupted', 'blocking_open', 'unverified', 'review_unavailable', 'unreviewed', 'clean'];
|
|
3
|
+
const OUTCOMES = ['no_change', 'interrupted', 'disputed', 'blocking_open', 'unverified', 'review_unavailable', 'unreviewed', 'clean'];
|
|
4
4
|
export function managedProjection(context: any, entries: any[]) {
|
|
5
5
|
if (context?.model?.provider !== 'agent_watch_managed') return {};
|
|
6
6
|
const last = (type: string) => entries.filter(e => e.type === 'custom' && e.customType === type).at(-1)?.data;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { memberQuestions } from "../../piagent-core/managed/member-questions.mjs";
|
|
2
|
+
|
|
3
|
+
// The main agent's questions to the member of a conversation this Gateway
|
|
4
|
+
// runs: the conversation's runtime is where they are answered, and a question
|
|
5
|
+
// waiting for its answer keeps the running turn alive (`progress`).
|
|
6
|
+
export function bindSessionQuestions(sessionId: string, progress: () => void): () => void {
|
|
7
|
+
return memberQuestions.bindSurface(sessionId, progress);
|
|
8
|
+
}
|
|
9
|
+
export function sessionQuestions(sessionId: string | null): { questions: ReturnType<typeof memberQuestions.pending> } {
|
|
10
|
+
return { questions: sessionId ? memberQuestions.pending(sessionId) : [] };
|
|
11
|
+
}
|
|
12
|
+
export function answerSessionQuestion(sessionId: string | null, questionRef: string, answer: unknown): unknown {
|
|
13
|
+
if (!sessionId) throw new Error("question-not-pending");
|
|
14
|
+
return memberQuestions.answer(sessionId, questionRef, answer);
|
|
15
|
+
}
|
|
@@ -6,6 +6,7 @@ import { activeSessionTask } from "../../piagent-core/extensions/task-state.js";
|
|
|
6
6
|
import { createSessionOperationSettlement } from "./session-operation-settlement.ts";
|
|
7
7
|
import { inspectTaskControlState } from "../../piagent-core/runtime/inspection/task-control-journal.ts";
|
|
8
8
|
import { piApprovalBroker, type ApprovalBrokerEvent } from "../../piagent-core/runtime/inspection/approval-broker.ts";
|
|
9
|
+
import { answerSessionQuestion, bindSessionQuestions, sessionQuestions } from "./session-questions.ts";
|
|
9
10
|
import { isUserConversationSession, projectRefForCwd, sessionRefForPath, type SessionOwnerProjection, type PiSessionInfo } from "./session-catalog.ts";
|
|
10
11
|
import { SessionLeaseStore, type SessionLeaseSnapshot } from "./session-lease-store.ts";
|
|
11
12
|
import { GatewayEventStore } from "./gateway-events.ts";
|
|
@@ -311,8 +312,10 @@ export class SessionRuntimeSupervisor {
|
|
|
311
312
|
}
|
|
312
313
|
#bindApproval(sessionRef: string, active: ActiveRuntime): void {
|
|
313
314
|
const authority = () => sessionApprovalAuthority(this.#key, sessionRef, active);
|
|
314
|
-
|
|
315
|
-
|
|
315
|
+
// The main agent's questions to the member are answered here too (session-questions.ts).
|
|
316
|
+
const unbindQuestions = bindSessionQuestions(active.info.id, () => active.watchdog?.progress()), unbindApproval = piApprovalBroker.bind({
|
|
317
|
+
cwd: active.info.cwd, rawSessionId: active.info.id, runtimeInstanceId: active.lease.runtimeInstanceRef!, authority });
|
|
318
|
+
active.unbindApproval = () => { unbindQuestions(); unbindApproval(); };
|
|
316
319
|
active.unsubscribeApproval = piApprovalBroker.subscribe(active.info.cwd, active.info.id, (event: ApprovalBrokerEvent) => {
|
|
317
320
|
active.watchdog?.progress();
|
|
318
321
|
const projection = piApprovalBroker.projection(active.info.cwd, active.info.id);
|
|
@@ -388,6 +391,8 @@ export class SessionRuntimeSupervisor {
|
|
|
388
391
|
const active = this.#active.get(sessionRef);
|
|
389
392
|
return active ? piApprovalBroker.detail(active.info.cwd, active.info.id, approvalRef) : null;
|
|
390
393
|
}
|
|
394
|
+
questions(sessionRef: string) { return sessionQuestions(this.#active.get(sessionRef)?.info.id ?? null); }
|
|
395
|
+
answerQuestion(sessionRef: string, questionRef: string, answer: unknown): unknown { return answerSessionQuestion(this.#active.get(sessionRef)?.info.id ?? null, questionRef, answer); }
|
|
391
396
|
async decideApproval(approvalRef: string, decision: unknown): Promise<unknown> {
|
|
392
397
|
for (const active of this.#active.values()) {
|
|
393
398
|
if (piApprovalBroker.detail(active.info.cwd, active.info.id, approvalRef)) {
|
|
@@ -81,6 +81,8 @@ export async function startLoopbackServer(options: {
|
|
|
81
81
|
executeControl?: (command: unknown) => unknown | Promise<unknown>;
|
|
82
82
|
executeAttachment?: (command: unknown) => unknown | Promise<unknown>;
|
|
83
83
|
executeApproval?: (approvalRef: string, decision: unknown) => unknown | Promise<unknown>;
|
|
84
|
+
readSessionQuestions?: (sessionRef: string) => unknown | Promise<unknown>;
|
|
85
|
+
answerSessionQuestion?: (sessionRef: string, questionRef: string, answer: unknown) => unknown | Promise<unknown>;
|
|
84
86
|
bootstrapTtlMs?: number;
|
|
85
87
|
sessionTtlMs?: number;
|
|
86
88
|
}): Promise<LoopbackServer> {
|
|
@@ -302,6 +304,33 @@ export async function startLoopbackServer(options: {
|
|
|
302
304
|
catch { return errorResponse(response, 503, "attachment-runtime-unavailable"); }
|
|
303
305
|
}
|
|
304
306
|
|
|
307
|
+
// The main agent's questions to the member of a conversation, and the answer.
|
|
308
|
+
const questionPath = /^\/api\/v1\/sessions\/([^/]+)\/questions(?:\/([^/]+)\/answer)?$/.exec(url.pathname);
|
|
309
|
+
if (request.method === "GET" && questionPath && !questionPath[2] && options.readSessionQuestions) {
|
|
310
|
+
if (!auth.authenticate(request)) return errorResponse(response, 401, "authentication-required");
|
|
311
|
+
const sessionRef = decodeURIComponent(questionPath[1]);
|
|
312
|
+
if (!CURSOR.test(sessionRef)) return errorResponse(response, 400, "invalid-session-ref");
|
|
313
|
+
try { return jsonResponse(response, 200, await options.readSessionQuestions(sessionRef)); }
|
|
314
|
+
catch { return errorResponse(response, 503, "questions-unavailable"); }
|
|
315
|
+
}
|
|
316
|
+
if (request.method === "POST" && questionPath && questionPath[2] && options.answerSessionQuestion) {
|
|
317
|
+
if (requestOrigin !== origin) return errorResponse(response, 403, "origin-required");
|
|
318
|
+
const mutationSession = auth.authorizeMutation(request);
|
|
319
|
+
if (!mutationSession) return errorResponse(response, 403, "mutation-authority-rejected");
|
|
320
|
+
if (!consumeControl(mutationSession.id, now)) return errorResponse(response, 429, "control-rate-limit");
|
|
321
|
+
if (!String(request.headers["content-type"] ?? "").toLowerCase().startsWith("application/json")) return errorResponse(response, 415, "content-type");
|
|
322
|
+
const sessionRef = decodeURIComponent(questionPath[1]), questionRef = decodeURIComponent(questionPath[2]);
|
|
323
|
+
if (!CURSOR.test(sessionRef) || !/^question\.[0-9a-f-]{36}$/.test(questionRef)) return errorResponse(response, 400, "invalid-question-ref");
|
|
324
|
+
let answer: unknown;
|
|
325
|
+
try { answer = JSON.parse((await requestBody(request, MAX_CONTROL_BODY_BYTES)).toString("utf8")); }
|
|
326
|
+
catch (error) { return errorResponse(response, (error as Error).message === "body-limit" ? 413 : 400, "invalid-question-answer"); }
|
|
327
|
+
try { return jsonResponse(response, 200, await options.answerSessionQuestion(sessionRef, questionRef, answer)); }
|
|
328
|
+
catch (error) {
|
|
329
|
+
const code = error instanceof Error ? error.message : "questions-unavailable";
|
|
330
|
+
return errorResponse(response, code === "question-not-pending" ? 409 : code === "question-answer-invalid" ? 400 : 503, code);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
305
334
|
if (request.method === "POST" && url.pathname.startsWith("/api/v1/approvals/") && url.pathname.endsWith("/decision") && options.executeApproval) {
|
|
306
335
|
if (requestOrigin !== origin) return errorResponse(response, 403, "origin-required");
|
|
307
336
|
const mutationSession = auth.authorizeMutation(request);
|
|
@@ -35,8 +35,10 @@ type TranscriptItem = { messageRef: string; parentMessageRef: string | null; rol
|
|
|
35
35
|
toolCalls: Array<{ toolCallRef: string; toolName: string; state: "requested" | "completed" | "failed" | "unknown";
|
|
36
36
|
summary?: ToolSummary; change?: ToolChange; result?: ToolResult }>; usage?: TurnUsage; model?: string; process?: HarnessProcess };
|
|
37
37
|
type HarnessFinding = { severity: "blocking" | "major" | "minor"; file: string; line: number | null; issue: string };
|
|
38
|
-
type
|
|
39
|
-
|
|
38
|
+
type HarnessIssue = { kind: "wrong_premise" | "conflicts_with_request" | "ambiguous" | "out_of_scope"; detail: string };
|
|
39
|
+
type HarnessProcess = { phase: "plan" | "verify" | "review" | "final" | "objection" | "answer" | "claims" | "rejudge" | "dispute"; outcome?: string; loop?: number; maxLoops?: number;
|
|
40
|
+
verified?: boolean; reviewed?: boolean; blockingOpen?: number; planOpen?: number; planSkipped?: true; verifyPolicy?: string; reviewPolicy?: string; reviewUnavailable?: string;
|
|
41
|
+
findings?: HarnessFinding[]; role?: "scout" | "research" | "verify" | "review"; by?: "main" | "harness"; disputes?: number; unchanged?: true; issues?: HarnessIssue[]; claims?: { claim: string }[] };
|
|
40
42
|
export type TranscriptDocument = { schemaVersion: 1; version: "piagent-webui-transcript-v1"; generatedAt: string; identity: TranscriptIdentity;
|
|
41
43
|
revision: TranscriptRevision; eventCursor: string; state: "ready" | "unavailable"; items: TranscriptItem[];
|
|
42
44
|
page: { beforeCursor: string | null; nextBeforeCursor: string | null; hasOlder: boolean; limit: number; truncated: boolean }; reasonCode: string | null };
|
|
@@ -163,10 +165,13 @@ function toolCalls(message: any, sessionRef: string, role: TranscriptItem["role"
|
|
|
163
165
|
}
|
|
164
166
|
// The harness's process notes ("agent-watch-process"): what it asked of the
|
|
165
167
|
// agent before a code-changing turn ended, and how that turn ended.
|
|
166
|
-
const PROCESS_OUTCOMES = ["no_change", "interrupted", "blocking_open", "unverified", "review_unavailable", "unreviewed", "clean"];
|
|
168
|
+
const PROCESS_OUTCOMES = ["no_change", "interrupted", "disputed", "blocking_open", "unverified", "review_unavailable", "unreviewed", "clean"];
|
|
169
|
+
// A helper's objection to its brief, a failed claim, a disagreement handed to the member.
|
|
170
|
+
const PROCESS_PHASES = ["plan", "verify", "review", "final", "objection", "answer", "claims", "rejudge", "dispute"];
|
|
171
|
+
const ISSUE_KINDS = ["wrong_premise", "conflicts_with_request", "ambiguous", "out_of_scope"], HELPER_ROLES = ["scout", "research", "verify", "review"];
|
|
167
172
|
const POLICY_MODES = ["off", "suggest", "require"];
|
|
168
173
|
function harnessProcess(details: any): HarnessProcess | null {
|
|
169
|
-
if (!
|
|
174
|
+
if (!PROCESS_PHASES.includes(details?.phase)) return null;
|
|
170
175
|
const loop = (n: unknown): n is number => Number.isInteger(n) && (n as number) >= 0 && (n as number) <= 3;
|
|
171
176
|
const out: HarnessProcess = { phase: details.phase };
|
|
172
177
|
if (PROCESS_OUTCOMES.includes(details.outcome)) out.outcome = details.outcome;
|
|
@@ -184,6 +189,14 @@ function harnessProcess(details: any): HarnessProcess | null {
|
|
|
184
189
|
.filter((f: any) => ["blocking", "major", "minor"].includes(f?.severity) && typeof f.issue === "string" && f.issue.trim()).slice(0, 10)
|
|
185
190
|
.map((f: any) => ({ severity: f.severity, file: safeText(String(f.file ?? "")).full.slice(0, 300), line: Number.isSafeInteger(f.line) && f.line > 0 ? f.line : null,
|
|
186
191
|
issue: safeText(f.issue).full.slice(0, 500) }));
|
|
192
|
+
if (HELPER_ROLES.includes(details.role)) out.role = details.role;
|
|
193
|
+
if (["main", "harness"].includes(details.by)) out.by = details.by;
|
|
194
|
+
if (Number.isInteger(details.disputes) && details.disputes >= 1) out.disputes = Math.min(details.disputes, 1000);
|
|
195
|
+
if (details.unchanged === true) out.unchanged = true;
|
|
196
|
+
if (Array.isArray(details.issues)) out.issues = details.issues.filter((i: any) => ISSUE_KINDS.includes(i?.kind) && typeof i.detail === "string" && i.detail.trim()).slice(0, 10)
|
|
197
|
+
.map((i: any) => ({ kind: i.kind, detail: safeText(i.detail).full.slice(0, 400) }));
|
|
198
|
+
if (Array.isArray(details.claims)) out.claims = details.claims.filter((c: any) => typeof c?.claim === "string" && c.claim.trim()).slice(0, 10)
|
|
199
|
+
.map((c: any) => ({ claim: safeText(c.claim).full.slice(0, 500) }));
|
|
187
200
|
return out;
|
|
188
201
|
}
|
|
189
202
|
function role(message: any): TranscriptItem["role"] | null {
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"steps": { "type": "array", "minItems": 1, "maxItems": 30, "items": { "type": "object", "additionalProperties": false, "required": ["step", "status"], "properties": {
|
|
98
98
|
"step": { "type": "string", "minLength": 1, "maxLength": 200 }, "status": { "enum": ["pending", "in_progress", "completed"] } } } } } },
|
|
99
99
|
"managedProcess": { "type": "object", "additionalProperties": false, "required": ["outcome", "verified", "reviewed", "blockingOpen"], "properties": {
|
|
100
|
-
"outcome": { "enum": ["no_change", "interrupted", "blocking_open", "unverified", "review_unavailable", "unreviewed", "clean"] },
|
|
100
|
+
"outcome": { "enum": ["no_change", "interrupted", "disputed", "blocking_open", "unverified", "review_unavailable", "unreviewed", "clean"] },
|
|
101
101
|
"verified": { "type": "boolean" }, "reviewed": { "type": "boolean" }, "blockingOpen": { "type": "integer", "minimum": 0, "maximum": 1000 }, "planSkipped": { "const": true } } },
|
|
102
102
|
"modelLabel": { "$ref": "common-v1.schema.json#/$defs/nullableDisplayText" },
|
|
103
103
|
"thinkingLevel": { "enum": ["off", "minimal", "low", "medium", "high", "xhigh", "max", "unknown"] },
|
|
@@ -157,8 +157,35 @@
|
|
|
157
157
|
"additionalProperties": false,
|
|
158
158
|
"required": ["phase"],
|
|
159
159
|
"properties": {
|
|
160
|
-
"phase": { "enum": ["plan", "verify", "review", "final"] },
|
|
161
|
-
"outcome": { "enum": ["no_change", "interrupted", "blocking_open", "unverified", "review_unavailable", "unreviewed", "clean"] },
|
|
160
|
+
"phase": { "enum": ["plan", "verify", "review", "final", "objection", "answer", "claims", "rejudge", "dispute"] },
|
|
161
|
+
"outcome": { "enum": ["no_change", "interrupted", "disputed", "blocking_open", "unverified", "review_unavailable", "unreviewed", "clean"] },
|
|
162
|
+
"role": { "enum": ["scout", "research", "verify", "review"] },
|
|
163
|
+
"by": { "enum": ["main", "harness"] },
|
|
164
|
+
"disputes": { "type": "integer", "minimum": 1, "maximum": 1000 },
|
|
165
|
+
"unchanged": { "const": true },
|
|
166
|
+
"issues": {
|
|
167
|
+
"type": "array",
|
|
168
|
+
"maxItems": 10,
|
|
169
|
+
"items": {
|
|
170
|
+
"type": "object",
|
|
171
|
+
"additionalProperties": false,
|
|
172
|
+
"required": ["kind", "detail"],
|
|
173
|
+
"properties": {
|
|
174
|
+
"kind": { "enum": ["wrong_premise", "conflicts_with_request", "ambiguous", "out_of_scope"] },
|
|
175
|
+
"detail": { "type": "string", "maxLength": 400 }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"claims": {
|
|
180
|
+
"type": "array",
|
|
181
|
+
"maxItems": 10,
|
|
182
|
+
"items": {
|
|
183
|
+
"type": "object",
|
|
184
|
+
"additionalProperties": false,
|
|
185
|
+
"required": ["claim"],
|
|
186
|
+
"properties": { "claim": { "type": "string", "maxLength": 500 } }
|
|
187
|
+
}
|
|
188
|
+
},
|
|
162
189
|
"loop": { "type": "integer", "minimum": 0, "maximum": 3 },
|
|
163
190
|
"maxLoops": { "type": "integer", "minimum": 0, "maximum": 3 },
|
|
164
191
|
"verified": { "type": "boolean" },
|