@akagilnc/pi-workflow-roles 0.1.5406 → 0.1.5475
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/CLAUDE.md +1 -1
- package/README.md +42 -55
- package/README.zh-CN.md +59 -73
- package/THIRD_PARTY_NOTICES.md +4 -7
- package/dist/acp-host/production-host.js +8261 -9073
- package/dist/collector-tool-schemas.js +1 -1
- package/dist/compliance-transport.js +2 -5
- package/dist/engine-detour-usage.js +3 -30
- package/dist/gatekeeper-role.js +4 -18
- package/dist/headless-host/description.js +0 -209
- package/dist/headless-host/production-host.js +6973 -8035
- package/dist/host-contracts.js +8 -0
- package/dist/migrate-book-topology.js +30 -27
- package/dist/packaged-role-registry.js +1 -9
- package/dist/pi/role-turn-host.js +37 -19
- package/dist/public-cli/auto-resume.js +29 -66
- package/dist/public-cli/countersign-run.js +4 -18
- package/dist/public-cli/instruction-seat-run.js +59 -76
- package/dist/public-cli/main.js +9844 -10720
- package/dist/public-cli/option-definitions.js +1 -1
- package/dist/public-cli/post-admission.js +204 -285
- package/dist/public-cli/process-cancel.js +8 -0
- package/dist/public-cli/run-lifecycle.js +24 -94
- package/dist/public-cli/settlement.js +614 -1524
- package/dist/public-cli/terminal.js +23 -53
- package/dist/public-role-summons.js +3 -8
- package/dist/receipt-delivery-policy.js +11 -12
- package/dist/run-terminal-artifacts.js +19 -14
- package/dist/serialize-thrown-value.js +7 -1
- package/dist/submission-ledger.js +21 -89
- package/package.json +1 -1
- package/packets/fixer-repair.md +3 -1
- package/packets/judge-apply.md +7 -16
- package/packets/judge-authority.md +7 -17
- package/packets/judge-plan.md +7 -18
- package/packets/judge-review.md +8 -22
- package/packets/judge-submission.md +4 -7
- package/resources/collector-bot-handbook.md +2 -2
- package/resources/diarist-collect.md +2 -2
- package/resources/engine-dispatch.md +2 -2
- package/resources/navigator-route-playbook.md +1 -1
- package/resources/secretariat-ticket-identity.md +1 -1
- package/scripts/build-package.mjs +0 -2
- package/souls/coder-output-guide.md +3 -3
- package/souls/coder.md +2 -3
- package/souls/doctor-auditor.md +0 -3
- package/souls/fixer-auditor.md +0 -2
- package/souls/fixer-output-guide.md +3 -3
- package/souls/fixer.md +2 -2
- package/souls/gate-output-guide.md +1 -1
- package/souls/quality-law.md +2 -0
- package/src/acp-host/role-turn-host.ts +168 -75
- package/src/collector-tool-schemas.ts +1 -1
- package/src/compliance-transport.ts +3 -6
- package/src/engine-detour-usage.ts +3 -33
- package/src/external-host-turn-loop.ts +81 -36
- package/src/gatekeeper-role.ts +5 -20
- package/src/headless-host/description.ts +4 -214
- package/src/headless-host/production-host.ts +2 -1
- package/src/headless-host/role-turn-host.ts +153 -186
- package/src/host-contracts.ts +13 -10
- package/src/navigator-public-session.ts +1 -1
- package/src/navigator-session-contracts.ts +1 -1
- package/src/package-contracts/gatekeeper-output.ts +1 -1
- package/src/package-contracts/navigator-output.ts +1 -1
- package/src/package-contracts/terminating-infrastructure.ts +1 -1
- package/src/packaged-role-registry.ts +2 -10
- package/src/pi/adapter.ts +0 -3
- package/src/pi/role-turn-host.ts +42 -28
- package/src/prepared-role-turn.ts +2 -0
- package/src/public-cli/auto-resume.ts +27 -66
- package/src/public-cli/countersign-run.ts +4 -22
- package/src/public-cli/instruction-seat-run.ts +79 -80
- package/src/public-cli/option-definitions.ts +1 -1
- package/src/public-cli/post-admission.ts +264 -340
- package/src/public-cli/process-cancel.ts +9 -0
- package/src/public-cli/run-lifecycle.ts +25 -126
- package/src/public-cli/settlement.ts +792 -1912
- package/src/public-cli/terminal.ts +29 -69
- package/src/public-role-summons.ts +3 -8
- package/src/receipt-delivery-policy.ts +11 -12
- package/src/role-envelope.ts +16 -9
- package/src/role-runtime.ts +14 -81
- package/src/run-terminal-artifacts.ts +21 -17
- package/src/serialize-thrown-value.ts +7 -1
- package/src/submission-ledger.ts +30 -116
- package/dist/audit-escalation.js +0 -7
- package/dist/pi/known-failure.js +0 -51
- package/dist/typed-provider-http.js +0 -57
- package/dist/upstream-error-testimony.js +0 -19
- package/src/pi/known-failure.ts +0 -76
- package/src/typed-provider-http.ts +0 -89
- package/src/upstream-error-testimony.ts +0 -45
|
@@ -3,7 +3,7 @@ import { openToolObject } from "./open-tool-schema.js";
|
|
|
3
3
|
import { withTerminatingOutputDeclarations } from "./package-contracts/terminating-infrastructure.js";
|
|
4
4
|
/**
|
|
5
5
|
* #1088: LLM gathers evidence via host CLI and submits the receipt.
|
|
6
|
-
* Field declarations guide the model — host must not pure-shape-reject (
|
|
6
|
+
* Field declarations guide the model — host must not pure-shape-reject (仓内 CLAUDE.md 开篇 / ADR 0057).
|
|
7
7
|
* Presence of `groups` remains the Collector terminal discriminator for settlement/analyst.
|
|
8
8
|
*/
|
|
9
9
|
export const collectorOutputBaseSchema = openToolObject(Type.Object({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { auditorRunDirectory } from "./auditor-dossier-tool.js";
|
|
2
2
|
import { officerConclusionReask } from "./gatekeeper-role.js";
|
|
3
3
|
import { receivedDiscriminator } from "./submission-errors.js";
|
|
4
|
-
import {
|
|
4
|
+
import { currentReplyRows } from "./public-cli/terminal.js";
|
|
5
5
|
import { readableGateItem } from "./readable-gate-item.js";
|
|
6
6
|
import { deliveryLimitFromConfig } from "./receipt-delivery-policy.js";
|
|
7
7
|
import { isRecord } from "./unknown-value.js";
|
|
@@ -78,10 +78,7 @@ async function projectAuditorTerminal(summoned) {
|
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
if (outcome.kind === "accepted") {
|
|
81
|
-
const rows =
|
|
82
|
-
outcome.payloads,
|
|
83
|
-
summoned.terminal?.submissions
|
|
84
|
-
);
|
|
81
|
+
const rows = currentReplyRows(summoned.terminal);
|
|
85
82
|
if (rows.length === 0) {
|
|
86
83
|
return readComplianceCandidate({}, usage);
|
|
87
84
|
}
|
|
@@ -27,8 +27,8 @@ export const ENGINE_DETOUR_CALL_KIND = "engine-detour-call";
|
|
|
27
27
|
export const ENGINE_DETOUR_TOOL_USAGE_FACT_KEY = "engineDetourToolUsage";
|
|
28
28
|
/**
|
|
29
29
|
* Run-relative sitian volume path for one detour call.
|
|
30
|
-
* Openable once the run directory is known
|
|
31
|
-
*
|
|
30
|
+
* Openable once the run directory is known from the terminal's run id.
|
|
31
|
+
* Contains no runId bytes itself (#108 + #537).
|
|
32
32
|
*/
|
|
33
33
|
export const ENGINE_DETOUR_CALL_RECORD_FILE_RELATIVE = `session/${ENGINE_DETOUR_CALL_KIND}/records.jsonl`;
|
|
34
34
|
/** stdout UTF-8 byte length (ticket-frozen metric; empty stdout is real 0). */
|
|
@@ -167,7 +167,7 @@ export async function readEngineDetourToolUsage(input) {
|
|
|
167
167
|
}
|
|
168
168
|
const pointer = {
|
|
169
169
|
identity: record.identity,
|
|
170
|
-
recordFile,
|
|
170
|
+
recordFile: ENGINE_DETOUR_CALL_RECORD_FILE_RELATIVE,
|
|
171
171
|
kind: record.kind,
|
|
172
172
|
level: record.level,
|
|
173
173
|
};
|
|
@@ -180,33 +180,6 @@ export async function readEngineDetourToolUsage(input) {
|
|
|
180
180
|
const calls = Array.from(callsByToolCallId.values());
|
|
181
181
|
return { callCount: calls.length, calls };
|
|
182
182
|
}
|
|
183
|
-
/**
|
|
184
|
-
* Project usage onto the public Terminal face.
|
|
185
|
-
* Non-resumable: keep absolute recordFile (runId already public via top-level runId).
|
|
186
|
-
* Resumable: keep an openable run-relative recordFile and identity with no runId
|
|
187
|
-
* bytes (#108 single disclosure + #537 AC8 reopen).
|
|
188
|
-
*/
|
|
189
|
-
export function projectEngineDetourToolUsageForPublicTerminal(usage, options) {
|
|
190
|
-
if (options.discloseRecordFile)
|
|
191
|
-
return usage;
|
|
192
|
-
return {
|
|
193
|
-
callCount: usage.callCount,
|
|
194
|
-
calls: usage.calls.map((call) => ({
|
|
195
|
-
toolCallId: call.toolCallId,
|
|
196
|
-
durationMs: call.durationMs,
|
|
197
|
-
...(call.code === undefined ? {} : { code: call.code }),
|
|
198
|
-
...(call.stdoutByteLength === undefined
|
|
199
|
-
? {}
|
|
200
|
-
: { stdoutByteLength: call.stdoutByteLength }),
|
|
201
|
-
recordPointer: {
|
|
202
|
-
identity: call.recordPointer.identity,
|
|
203
|
-
kind: call.recordPointer.kind,
|
|
204
|
-
level: call.recordPointer.level,
|
|
205
|
-
recordFile: ENGINE_DETOUR_CALL_RECORD_FILE_RELATIVE,
|
|
206
|
-
},
|
|
207
|
-
})),
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
183
|
/** One shared invocation.json reader (ENOENT → undefined; other errors propagate). */
|
|
211
184
|
function readInvocationRecord(runDirectory) {
|
|
212
185
|
try {
|
package/dist/gatekeeper-role.js
CHANGED
|
@@ -5,8 +5,10 @@ import { INSPECTOR_OUTPUT_TOOL_NAME } from "./inspector-contracts.js";
|
|
|
5
5
|
import { REVIEW_QUEUE_STATUSES, REVIEW_SUBMISSION_OUTPUT_TOOL_NAME } from "./review-submission.js";
|
|
6
6
|
import { GATEKEEPER_OUTPUT_TOOL_NAME } from "./package-contracts/gatekeeper-output.js";
|
|
7
7
|
import { roleSubmissionDeclaration } from "./role-submission-declarations.js";
|
|
8
|
+
import { currentReplyRows } from "./public-cli/terminal.js";
|
|
8
9
|
import { runIdFromRunDirectory } from "./run-terminal-artifacts.js";
|
|
9
10
|
import { isRecord } from "./unknown-value.js";
|
|
11
|
+
import { serializeThrownValue } from "./serialize-thrown-value.js";
|
|
10
12
|
export const INSPECTOR_OUTPUT_TOOL = INSPECTOR_OUTPUT_TOOL_NAME;
|
|
11
13
|
export const NOTARY_OUTPUT_TOOL = REVIEW_SUBMISSION_OUTPUT_TOOL_NAME;
|
|
12
14
|
function gateSeatLabel(stage) {
|
|
@@ -31,11 +33,6 @@ export { GatekeeperDecisionError } from "./submission-errors.js";
|
|
|
31
33
|
* the shared contract module (with infrastructure-failure declaration).
|
|
32
34
|
*/
|
|
33
35
|
export const GATEKEEPER_TOOL_SPEC = roleSubmissionDeclaration("gatekeeper");
|
|
34
|
-
function failureReason(error) {
|
|
35
|
-
if (error instanceof AggregateError)
|
|
36
|
-
return error.errors.map(failureReason).join("; ");
|
|
37
|
-
return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
38
|
-
}
|
|
39
36
|
/** Original decision bytes — no sentinel replacement (#836). */
|
|
40
37
|
function retainedReceipt(decision) {
|
|
41
38
|
return decision;
|
|
@@ -72,17 +69,6 @@ function projectOfficerDecision(officer, decision, fallbackStatus) {
|
|
|
72
69
|
* identity. Never guess from undivided submissions — sole row included — history
|
|
73
70
|
* stays on terminal.submissions (#836 presentation).
|
|
74
71
|
*/
|
|
75
|
-
function thisCourtOfficerPayloads(terminal) {
|
|
76
|
-
const outcome = terminal?.roleOutcome;
|
|
77
|
-
// This-court identity only from accepted/audit_escalation settlement payloads
|
|
78
|
-
// (#879). Failure history is not this-court (#953) — it rides submissions.
|
|
79
|
-
if (outcome !== undefined && (outcome.kind === "accepted" || outcome.kind === "audit_escalation")) {
|
|
80
|
-
if (outcome.payloads !== undefined && outcome.payloads.length > 0)
|
|
81
|
-
return outcome.payloads;
|
|
82
|
-
}
|
|
83
|
-
// No sole-row identity guess: undivided submissions are not this-court (#879).
|
|
84
|
-
return [];
|
|
85
|
-
}
|
|
86
72
|
/**
|
|
87
73
|
* Failure/transport channel surfaces recorded history beside the failure
|
|
88
74
|
* (#836 A.3) via the historical carrier only (#953 — not failure.payloads).
|
|
@@ -130,7 +116,7 @@ function withOfficerRunId(result, summoned) {
|
|
|
130
116
|
function projectOfficerTerminal(officer, summoned) {
|
|
131
117
|
const terminal = summoned.terminal;
|
|
132
118
|
const outcome = terminal?.roleOutcome;
|
|
133
|
-
const thisCourt =
|
|
119
|
+
const thisCourt = currentReplyRows(terminal);
|
|
134
120
|
if (outcome === undefined) {
|
|
135
121
|
const detail = summoned.stderr ?? "";
|
|
136
122
|
const failurePayloads = officerFailurePayloads(terminal);
|
|
@@ -221,7 +207,7 @@ export async function projectGatekeeperRun(options) {
|
|
|
221
207
|
catch (error) {
|
|
222
208
|
return {
|
|
223
209
|
officer,
|
|
224
|
-
result: { status: "transport_failure", stage: officer, reason:
|
|
210
|
+
result: { status: "transport_failure", stage: officer, reason: serializeThrownValue(error) },
|
|
225
211
|
};
|
|
226
212
|
}
|
|
227
213
|
return {
|
|
@@ -54,215 +54,6 @@ function codexTomlStringTable(entries) {
|
|
|
54
54
|
const parts = Object.entries(entries).map(([key, value]) => `${key}=${codexTomlString(value)}`);
|
|
55
55
|
return `{${parts.join(",")}}`;
|
|
56
56
|
}
|
|
57
|
-
/**
|
|
58
|
-
* Free-form JSON leaf for Type.Unknown under Codex strict transport.
|
|
59
|
-
* Strict rejects bare untyped nodes and root-level additionalProperties:true;
|
|
60
|
-
* a $defs anyOf of JSON values (with nested additionalProperties as $ref)
|
|
61
|
-
* is accepted and keeps array/object receipts expressible.
|
|
62
|
-
* Package code still does not validate or reject the receipt against schema.
|
|
63
|
-
*/
|
|
64
|
-
const CODEX_JSON_VALUE_DEF = "codexJsonValue";
|
|
65
|
-
const CODEX_JSON_VALUE_REF = `#/$defs/${CODEX_JSON_VALUE_DEF}`;
|
|
66
|
-
const CODEX_JSON_VALUE_SCHEMA = Object.freeze({
|
|
67
|
-
anyOf: Object.freeze([
|
|
68
|
-
Object.freeze({ type: "string" }),
|
|
69
|
-
Object.freeze({ type: "number" }),
|
|
70
|
-
Object.freeze({ type: "boolean" }),
|
|
71
|
-
Object.freeze({ type: "null" }),
|
|
72
|
-
Object.freeze({ type: "array", items: Object.freeze({ $ref: CODEX_JSON_VALUE_REF }) }),
|
|
73
|
-
Object.freeze({
|
|
74
|
-
type: "object",
|
|
75
|
-
properties: Object.freeze({}),
|
|
76
|
-
required: Object.freeze([]),
|
|
77
|
-
additionalProperties: Object.freeze({ $ref: CODEX_JSON_VALUE_REF }),
|
|
78
|
-
}),
|
|
79
|
-
]),
|
|
80
|
-
});
|
|
81
|
-
/**
|
|
82
|
-
* Derive a Codex/OpenAI-strict transport schema from the package open schema.
|
|
83
|
-
* Legal open schema is untouched; this is a host-only transmission projection
|
|
84
|
-
* (#646 / 0057 法意 / 0054 strict): every object closes, every property is
|
|
85
|
-
* required, and only originally-optional fields become a null union (official
|
|
86
|
-
* guidance: emulate optional via type|null). Originally-required fields stay
|
|
87
|
-
* non-nullable. Nested open-tool anyOf wrappers are flattened so every branch
|
|
88
|
-
* carries `type`. Type.Unknown / description-only leaves become a free JSON
|
|
89
|
-
* $ref. Package code still does not validate or reject the receipt.
|
|
90
|
-
*/
|
|
91
|
-
export function closeJsonSchemaForCodex(schema) {
|
|
92
|
-
const closed = closeSchemaNode(schema);
|
|
93
|
-
const existingDefs = isRecord(closed.$defs)
|
|
94
|
-
? closed.$defs
|
|
95
|
-
: {};
|
|
96
|
-
return {
|
|
97
|
-
...closed,
|
|
98
|
-
$defs: {
|
|
99
|
-
...existingDefs,
|
|
100
|
-
[CODEX_JSON_VALUE_DEF]: CODEX_JSON_VALUE_SCHEMA,
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
/** Pure null leaf only — composite type|null is stripped in-leaf, not dropped whole. */
|
|
105
|
-
function isPureNullTypeSchema(value) {
|
|
106
|
-
if (!isRecord(value))
|
|
107
|
-
return false;
|
|
108
|
-
if (value.type === "null")
|
|
109
|
-
return true;
|
|
110
|
-
if (Array.isArray(value.type) && value.type.length > 0 && value.type.every((t) => t === "null")) {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Within a leaf, remove null members from type arrays.
|
|
117
|
-
* Required edges keep the non-null type(s); optional edges re-add null once.
|
|
118
|
-
*/
|
|
119
|
-
function stripNullFromLeafType(leaf) {
|
|
120
|
-
if (!isRecord(leaf) || !Array.isArray(leaf.type))
|
|
121
|
-
return leaf;
|
|
122
|
-
const nonNull = leaf.type.filter((t) => t !== "null");
|
|
123
|
-
if (nonNull.length === leaf.type.length)
|
|
124
|
-
return leaf;
|
|
125
|
-
if (nonNull.length === 0)
|
|
126
|
-
return { ...leaf, type: "null" };
|
|
127
|
-
if (nonNull.length === 1)
|
|
128
|
-
return { ...leaf, type: nonNull[0] };
|
|
129
|
-
return { ...leaf, type: nonNull };
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Flatten nested anyOf wrappers into concrete leaf schemas.
|
|
133
|
-
* Open-tool unions often wrap leaves in description-only anyOf shells that
|
|
134
|
-
* lack `type`; strict structured output rejects those intermediate nodes.
|
|
135
|
-
*/
|
|
136
|
-
function flattenUnionLeaves(schema) {
|
|
137
|
-
if (!isRecord(schema))
|
|
138
|
-
return [schema];
|
|
139
|
-
if (Array.isArray(schema.anyOf)) {
|
|
140
|
-
return schema.anyOf.flatMap(flattenUnionLeaves);
|
|
141
|
-
}
|
|
142
|
-
return [schema];
|
|
143
|
-
}
|
|
144
|
-
/** Drop pure-null leaves after in-leaf null stripping; optional edges re-add null once. */
|
|
145
|
-
function nonNullLeaves(schema) {
|
|
146
|
-
return flattenUnionLeaves(schema)
|
|
147
|
-
.map(stripNullFromLeafType)
|
|
148
|
-
.filter((leaf) => !isPureNullTypeSchema(leaf));
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Property edge under strict transport.
|
|
152
|
-
* Required → closed non-null leaf(s). Optional → closed leaf(s) + null.
|
|
153
|
-
*/
|
|
154
|
-
function closePropertySchema(schema, optional) {
|
|
155
|
-
const description = isRecord(schema) && typeof schema.description === "string"
|
|
156
|
-
? { description: schema.description }
|
|
157
|
-
: {};
|
|
158
|
-
const leaves = nonNullLeaves(schema).map(closeSchemaNode);
|
|
159
|
-
if (leaves.length === 0)
|
|
160
|
-
return { type: "null", ...description };
|
|
161
|
-
if (!optional) {
|
|
162
|
-
const closed = leaves.length === 1 ? leaves[0] : { anyOf: leaves };
|
|
163
|
-
// Codex strict does not allow sibling keys on $ref; put property guidance
|
|
164
|
-
// on an enclosing schema instead of losing it or weakening the free-JSON ref.
|
|
165
|
-
if (isRecord(closed) && typeof closed.$ref === "string") {
|
|
166
|
-
return { anyOf: [closed], ...description };
|
|
167
|
-
}
|
|
168
|
-
return isRecord(closed) ? { ...closed, ...description } : closed;
|
|
169
|
-
}
|
|
170
|
-
return { anyOf: [...leaves, { type: "null" }], ...description };
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Strict generators require every schema node to declare `type` (or a $ref).
|
|
174
|
-
* Type.Unknown / description-only leaves → free JSON $ref (not string): array
|
|
175
|
-
* and object receipts stay expressible under --output-schema.
|
|
176
|
-
*/
|
|
177
|
-
function ensureTypedLeaf(schema) {
|
|
178
|
-
if (schema.type !== undefined)
|
|
179
|
-
return schema;
|
|
180
|
-
if (typeof schema.$ref === "string")
|
|
181
|
-
return schema;
|
|
182
|
-
if (isRecord(schema.properties) || schema.additionalProperties !== undefined) {
|
|
183
|
-
return { ...schema, type: "object" };
|
|
184
|
-
}
|
|
185
|
-
if (schema.items !== undefined) {
|
|
186
|
-
return { ...schema, type: "array" };
|
|
187
|
-
}
|
|
188
|
-
if (schema.const !== undefined) {
|
|
189
|
-
const value = schema.const;
|
|
190
|
-
if (value === null)
|
|
191
|
-
return { ...schema, type: "null" };
|
|
192
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
193
|
-
return { ...schema, type: typeof value };
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
|
|
197
|
-
const sample = schema.enum.find((item) => item !== null);
|
|
198
|
-
if (typeof sample === "string" || typeof sample === "number" || typeof sample === "boolean") {
|
|
199
|
-
return { ...schema, type: typeof sample };
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
// Free JSON via $defs. $ref must stand alone (strict rejects sibling keys).
|
|
203
|
-
return { $ref: CODEX_JSON_VALUE_REF };
|
|
204
|
-
}
|
|
205
|
-
function originalRequiredNames(node) {
|
|
206
|
-
if (!Array.isArray(node.required))
|
|
207
|
-
return new Set();
|
|
208
|
-
return new Set(node.required.filter((item) => typeof item === "string"));
|
|
209
|
-
}
|
|
210
|
-
function closeSchemaNode(node) {
|
|
211
|
-
if (!isRecord(node))
|
|
212
|
-
return node;
|
|
213
|
-
// Already a ref (free-JSON leaf or pre-existing) — do not retype.
|
|
214
|
-
if (typeof node.$ref === "string")
|
|
215
|
-
return node;
|
|
216
|
-
// Union node: flatten then close each concrete leaf (do not keep untyped shells).
|
|
217
|
-
if (Array.isArray(node.anyOf)) {
|
|
218
|
-
const leaves = nonNullLeaves(node).map(closeSchemaNode);
|
|
219
|
-
if (leaves.length === 0)
|
|
220
|
-
return { type: "null" };
|
|
221
|
-
if (leaves.length === 1)
|
|
222
|
-
return leaves[0];
|
|
223
|
-
return { anyOf: leaves };
|
|
224
|
-
}
|
|
225
|
-
let out = { ...node };
|
|
226
|
-
if (node.items !== undefined) {
|
|
227
|
-
out.items = closeSchemaNode(node.items);
|
|
228
|
-
}
|
|
229
|
-
if (isRecord(node.$defs)) {
|
|
230
|
-
out.$defs = Object.fromEntries(Object.entries(node.$defs).map(([key, value]) => [key, closeSchemaNode(value)]));
|
|
231
|
-
}
|
|
232
|
-
const hasProperties = isRecord(node.properties);
|
|
233
|
-
const isObjectType = node.type === "object"
|
|
234
|
-
|| (Array.isArray(node.type) && node.type.includes("object"))
|
|
235
|
-
|| hasProperties
|
|
236
|
-
|| node.additionalProperties !== undefined;
|
|
237
|
-
if (hasProperties) {
|
|
238
|
-
const props = node.properties;
|
|
239
|
-
const wasRequired = originalRequiredNames(node);
|
|
240
|
-
const closedProps = {};
|
|
241
|
-
const required = [];
|
|
242
|
-
for (const [name, propSchema] of Object.entries(props)) {
|
|
243
|
-
required.push(name);
|
|
244
|
-
closedProps[name] = closePropertySchema(propSchema, !wasRequired.has(name));
|
|
245
|
-
}
|
|
246
|
-
out.properties = closedProps;
|
|
247
|
-
out.required = required;
|
|
248
|
-
out.additionalProperties = false;
|
|
249
|
-
if (out.type === undefined)
|
|
250
|
-
out.type = "object";
|
|
251
|
-
// Object nodes must not also carry residual anyOf from the open copy.
|
|
252
|
-
delete out.anyOf;
|
|
253
|
-
}
|
|
254
|
-
else if (isObjectType) {
|
|
255
|
-
out.additionalProperties = false;
|
|
256
|
-
if (!Array.isArray(out.required))
|
|
257
|
-
out.required = [];
|
|
258
|
-
if (out.type === undefined)
|
|
259
|
-
out.type = "object";
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
out = ensureTypedLeaf(out);
|
|
263
|
-
}
|
|
264
|
-
return out;
|
|
265
|
-
}
|
|
266
57
|
/**
|
|
267
58
|
* Project shared-envelope MCP rows into `codex -c mcp_servers.<name>.*` argv pairs.
|
|
268
59
|
* Dot-path + TOML values per official config-advanced; spawn argv (no shell).
|