@intentius/chant 0.104.3 → 0.105.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/dist/codegen/generate-runtime-index.d.ts +3 -1
- package/dist/codegen/generate-runtime-index.d.ts.map +1 -1
- package/dist/op/activities/factory.d.ts +1 -1
- package/dist/op/activities/factory.d.ts.map +1 -1
- package/dist/vendor/js-yaml-load.d.ts.map +1 -1
- package/dist/workspace/agent-cli.d.ts +14 -8
- package/dist/workspace/agent-cli.d.ts.map +1 -1
- package/dist/workspace/broker-protocol.d.ts +65 -3
- package/dist/workspace/broker-protocol.d.ts.map +1 -1
- package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts.map +1 -1
- package/dist/workspace/conformance/broker.d.ts.map +1 -1
- package/dist/workspace/declaration.d.ts +9 -2
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +32 -6
- package/dist/workspace/export.d.ts.map +1 -1
- package/dist/workspace/factory-rules.d.ts +9 -4
- package/dist/workspace/factory-rules.d.ts.map +1 -1
- package/dist/workspace/reason-codes.d.ts +1 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/write-scope.d.ts +7 -5
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/codegen/generate-runtime-index.test.ts +3 -3
- package/src/codegen/generate-runtime-index.ts +5 -3
- package/src/op/activities/factory.ts +1 -1
- package/src/op/factory.e2e.test.ts +16 -5
- package/src/vendor/js-yaml-load.ts +14 -63
- package/src/workspace/agent-cli.ts +26 -9
- package/src/workspace/agent.schema.json +33 -21
- package/src/workspace/broker-protocol.schema.json +45 -3
- package/src/workspace/broker-protocol.test.ts +45 -0
- package/src/workspace/broker-protocol.ts +131 -5
- package/src/workspace/conformance/__fixtures__/reference-broker.ts +3 -1
- package/src/workspace/conformance/broker.ts +22 -0
- package/src/workspace/declaration.schema.json +32 -6
- package/src/workspace/declaration.ts +29 -15
- package/src/workspace/export.ts +10 -3
- package/src/workspace/factory-rules.test.ts +6 -1
- package/src/workspace/factory-rules.ts +10 -3
- package/src/workspace/reason-codes.ts +1 -1
- package/src/workspace/write-scope.test.ts +18 -1
- package/src/workspace/write-scope.ts +15 -10
|
@@ -25,9 +25,9 @@ describe("generateRuntimeIndex", () => {
|
|
|
25
25
|
const result = generateRuntimeIndex(resources, properties, baseConfig);
|
|
26
26
|
|
|
27
27
|
// Resources sorted: Alarm before Bucket
|
|
28
|
-
expect(result).toContain('export const Alarm = createResource("AWS::CloudWatch::Alarm", "test", {});');
|
|
29
|
-
expect(result).toContain('export const Bucket = createResource("AWS::S3::Bucket", "test", {"arn":"Arn"});');
|
|
30
|
-
expect(result).toContain('export const Bucket_Versioning = createProperty("AWS::S3::Bucket.VersioningConfiguration", "test");');
|
|
28
|
+
expect(result).toContain('export const Alarm = /* @__PURE__ */ createResource("AWS::CloudWatch::Alarm", "test", {});');
|
|
29
|
+
expect(result).toContain('export const Bucket = /* @__PURE__ */ createResource("AWS::S3::Bucket", "test", {"arn":"Arn"});');
|
|
30
|
+
expect(result).toContain('export const Bucket_Versioning = /* @__PURE__ */ createProperty("AWS::S3::Bucket.VersioningConfiguration", "test");');
|
|
31
31
|
|
|
32
32
|
// Alarm should come before Bucket in the output
|
|
33
33
|
const alarmIdx = result.indexOf("Alarm");
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* Generic runtime index generator for lexicon packages.
|
|
3
3
|
*
|
|
4
4
|
* Produces the `index.ts` file containing factory constructor exports
|
|
5
|
-
* (`createResource`, `createProperty`) and collision-safe re-exports.
|
|
5
|
+
* (`createResource`, `createProperty`) and collision-safe re-exports. Each
|
|
6
|
+
* factory call is marked pure, so a bundler drops the classes a caller does
|
|
7
|
+
* not import instead of keeping every one.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
export interface RuntimeIndexConfig {
|
|
@@ -55,7 +57,7 @@ export function generateRuntimeIndex(
|
|
|
55
57
|
for (const { tsName, resourceType, attrs } of sortedResources) {
|
|
56
58
|
const attrsStr = JSON.stringify(attrs);
|
|
57
59
|
lines.push(
|
|
58
|
-
`export const ${tsName} = createResource(${JSON.stringify(resourceType)}, ${JSON.stringify(config.lexiconName)}, ${attrsStr});`,
|
|
60
|
+
`export const ${tsName} = /* @__PURE__ */ createResource(${JSON.stringify(resourceType)}, ${JSON.stringify(config.lexiconName)}, ${attrsStr});`,
|
|
59
61
|
);
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -65,7 +67,7 @@ export function generateRuntimeIndex(
|
|
|
65
67
|
const sortedProperties = [...properties].sort((a, b) => a.tsName.localeCompare(b.tsName));
|
|
66
68
|
for (const { tsName, resourceType } of sortedProperties) {
|
|
67
69
|
lines.push(
|
|
68
|
-
`export const ${tsName} = createProperty(${JSON.stringify(resourceType)}, ${JSON.stringify(config.lexiconName)});`,
|
|
70
|
+
`export const ${tsName} = /* @__PURE__ */ createProperty(${JSON.stringify(resourceType)}, ${JSON.stringify(config.lexiconName)});`,
|
|
69
71
|
);
|
|
70
72
|
}
|
|
71
73
|
|
|
@@ -370,7 +370,7 @@ async function commitAll(worktree: string, subject: string, item: string, kindNa
|
|
|
370
370
|
|
|
371
371
|
// ── the activities ───────────────────────────────────────────────────────────
|
|
372
372
|
|
|
373
|
-
/** Which work items are buildable now (#3406 rule 1). */
|
|
373
|
+
/** Which work items are buildable now (#3406 rule 1): those that name a contract, asks and intent builds (#3503). */
|
|
374
374
|
export async function factoryPick(args: FactoryPickArgs = {}): Promise<FactoryPickResult> {
|
|
375
375
|
const cwd = args.cwd ?? process.cwd();
|
|
376
376
|
const { pickable, readyKey } = await import("../../workspace/factory-rules");
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The factory reference Op (#3406, ws-087), run through the local executor
|
|
3
3
|
* with stub hooks on a small workspace that carries the reference
|
|
4
|
-
* workspace's decision, work and
|
|
4
|
+
* workspace's decision, work, answer and contract kinds and its points, its
|
|
5
|
+
* work kind linked to the contracts, as the factory builds only an item that
|
|
6
|
+
* names a contract, an ask or an intent build (#3503): one item from
|
|
5
7
|
* pick to done, one retry after a failed build, and one ask the understand
|
|
6
8
|
* point refuses. The rules themselves are tested in
|
|
7
9
|
* ../workspace/factory-rules.test.ts.
|
|
@@ -53,10 +55,19 @@ function workspace(dir: string): void {
|
|
|
53
55
|
git(["config", "user.email", "t@chant.dev"], dir);
|
|
54
56
|
git(["config", "user.name", "T"], dir);
|
|
55
57
|
git(["config", "commit.gpgsign", "false"], dir);
|
|
56
|
-
for (const d of ["decisions", "work", "answers"]) mkdirSync(join(dir, d), { recursive: true });
|
|
58
|
+
for (const d of ["decisions", "work", "answers", "contracts"]) mkdirSync(join(dir, d), { recursive: true });
|
|
57
59
|
for (const f of ["decisions/decision.kind.mjs", "decisions/decision.schema.json", "decisions/points.json", "work/work.kind.mjs", "work/work.schema.json", "answers/answer.kind.mjs", "answers/answer.schema.json"]) {
|
|
58
60
|
cpSync(join(REF, f), join(dir, f));
|
|
59
61
|
}
|
|
62
|
+
for (const f of ["contract.kind.mjs", "contract.schema.json"]) cpSync(join(REF, "design", "contracts", f), join(dir, "contracts", f));
|
|
63
|
+
const kindFile = join(dir, "work", "work.kind.mjs");
|
|
64
|
+
const answers = 'answers: "../answers/answer.kind.mjs",';
|
|
65
|
+
if (!readFileSync(kindFile, "utf-8").includes(answers)) throw new Error("the reference work kind no longer names its answer kind as the fixture expects");
|
|
66
|
+
writeFileSync(kindFile, readFileSync(kindFile, "utf-8").replace(answers, `${answers}\n contract: { field: "contract", kind: "../contracts/contract.kind.mjs" },`));
|
|
67
|
+
writeFileSync(
|
|
68
|
+
join(dir, "contracts", "C-001-the-feature.md"),
|
|
69
|
+
`---\n${JSON.stringify({ schema: 1, id: "C-001", title: "The feature", state: "approved", criteria: [{ id: "AC-1", text: "the feature exists" }], checks: [], reviews: [], approved_by: "alice" }, null, 2)}\n---\n\n# The feature\n`,
|
|
70
|
+
);
|
|
60
71
|
writeFileSync(
|
|
61
72
|
join(dir, "chant.workspace.json"),
|
|
62
73
|
JSON.stringify({
|
|
@@ -71,8 +82,8 @@ function workspace(dir: string): void {
|
|
|
71
82
|
mkdirSync(join(dir, "hooks"), { recursive: true });
|
|
72
83
|
writeFileSync(join(dir, "hooks", "builder.cjs"), BUILDER);
|
|
73
84
|
writeFileSync(join(dir, "hooks", "check.cjs"), CHECK);
|
|
74
|
-
writeFileSync(join(dir, "work", "W-101-the-feature.md"), item("W-101", { acceptance: [{ id: "AC-1", text: "app/W-101.txt exists", verification: "unit" }] }));
|
|
75
|
-
writeFileSync(join(dir, "work", "W-102-the-retried-one.md"), item("W-102", { acceptance: [{ id: "AC-1", text: "app/W-102.txt exists", verification: "unit" }] }));
|
|
85
|
+
writeFileSync(join(dir, "work", "W-101-the-feature.md"), item("W-101", { contract: "C-001", acceptance: [{ id: "AC-1", text: "app/W-101.txt exists", verification: "unit" }] }));
|
|
86
|
+
writeFileSync(join(dir, "work", "W-102-the-retried-one.md"), item("W-102", { contract: "C-001", acceptance: [{ id: "AC-1", text: "app/W-102.txt exists", verification: "unit" }] }));
|
|
76
87
|
writeFileSync(join(dir, "work", "W-103-an-ask.md"), item("W-103", { state: "proposed", source: { ask: { said: "Make the page purple.", by: "alice", via: "hud" } } }));
|
|
77
88
|
git(["add", "-A"], dir);
|
|
78
89
|
git(["commit", "-q", "-m", "the workspace"], dir);
|
|
@@ -177,7 +188,7 @@ describe("the factory Op's seams for an orchestrator (studio#382)", () => {
|
|
|
177
188
|
// The prepare hook writes W-110 in the checkout and leaves it uncommitted; the others are taken out of the way.
|
|
178
189
|
writeFileSync(
|
|
179
190
|
join(dir, "hooks", "prepare.cjs"),
|
|
180
|
-
`require("node:fs").writeFileSync("work/W-110-prepared.md", ${JSON.stringify(item("W-110", { acceptance: [{ id: "AC-1", text: "one", verification: "unit" }, { id: "AC-2", text: "two", verification: "unit" }] }))});`,
|
|
191
|
+
`require("node:fs").writeFileSync("work/W-110-prepared.md", ${JSON.stringify(item("W-110", { contract: "C-001", acceptance: [{ id: "AC-1", text: "one", verification: "unit" }, { id: "AC-2", text: "two", verification: "unit" }] }))});`,
|
|
181
192
|
);
|
|
182
193
|
for (const id of ["W-101", "W-102"]) writeFileSync(join(dir, ".fail-" + id), "");
|
|
183
194
|
git(["add", "-A"], dir);
|
|
@@ -29,7 +29,10 @@
|
|
|
29
29
|
* timestamp, omap, pairs, set), the snippet renderer, the schema classes and
|
|
30
30
|
* the loader's option handling are gone. The reader always runs in json mode
|
|
31
31
|
* (a repeated mapping key takes the last value) and takes its type list from
|
|
32
|
-
* the caller.
|
|
32
|
+
* the caller. With the options fixed, the branches only another setting
|
|
33
|
+
* reached (the duplicate key error, the legacy line break check) and the
|
|
34
|
+
* warnings, which nothing listened to, are gone too. The parsing code itself
|
|
35
|
+
* is otherwise unchanged. It is kept in one file
|
|
33
36
|
* because js-yaml's ES module wraps each of its files in an init function a
|
|
34
37
|
* bundler cannot shake, so importing it costs the whole library.
|
|
35
38
|
*/
|
|
@@ -214,7 +217,6 @@ const CHOMPING_KEEP = 3
|
|
|
214
217
|
|
|
215
218
|
// eslint-disable-next-line no-control-regex
|
|
216
219
|
const PATTERN_NON_PRINTABLE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/
|
|
217
|
-
const PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/
|
|
218
220
|
// eslint-disable-next-line no-useless-escape
|
|
219
221
|
const PATTERN_FLOW_INDICATORS = /[,\[\]{}]/
|
|
220
222
|
// eslint-disable-next-line no-useless-escape
|
|
@@ -338,12 +340,8 @@ for (let i = 0; i < 256; i++) {
|
|
|
338
340
|
function State (input, types) {
|
|
339
341
|
this.input = input
|
|
340
342
|
|
|
341
|
-
this.filename = null
|
|
342
343
|
this.implicitTypes = types.implicit
|
|
343
344
|
this.typeMap = compileTypes(types.implicit, types.explicit)
|
|
344
|
-
// json mode: a duplicate mapping key takes the last value
|
|
345
|
-
this.json = true
|
|
346
|
-
this.legacy = false
|
|
347
345
|
this.maxDepth = 100
|
|
348
346
|
this.maxTotalMergeKeys = 10000
|
|
349
347
|
|
|
@@ -364,7 +362,6 @@ function State (input, types) {
|
|
|
364
362
|
|
|
365
363
|
/*
|
|
366
364
|
this.version;
|
|
367
|
-
this.checkLineBreaks;
|
|
368
365
|
this.tagMap;
|
|
369
366
|
this.anchorMap;
|
|
370
367
|
this.tag;
|
|
@@ -375,7 +372,7 @@ function State (input, types) {
|
|
|
375
372
|
|
|
376
373
|
function generateError (state, message) {
|
|
377
374
|
const mark = {
|
|
378
|
-
name:
|
|
375
|
+
name: null,
|
|
379
376
|
buffer: state.input.slice(0, -1), // omit trailing \0
|
|
380
377
|
position: state.position,
|
|
381
378
|
line: state.line,
|
|
@@ -389,8 +386,6 @@ function throwError (state, message) {
|
|
|
389
386
|
throw generateError(state, message)
|
|
390
387
|
}
|
|
391
388
|
|
|
392
|
-
function throwWarning () {}
|
|
393
|
-
|
|
394
389
|
function storeAnchor (state, name, value) {
|
|
395
390
|
const transactions = state.anchorMapTransactions
|
|
396
391
|
|
|
@@ -489,18 +484,12 @@ const directiveHandlers = {
|
|
|
489
484
|
}
|
|
490
485
|
|
|
491
486
|
const major = parseInt(match[1], 10)
|
|
492
|
-
const minor = parseInt(match[2], 10)
|
|
493
487
|
|
|
494
488
|
if (major !== 1) {
|
|
495
489
|
throwError(state, 'unacceptable YAML version of the document')
|
|
496
490
|
}
|
|
497
491
|
|
|
498
492
|
state.version = args[0]
|
|
499
|
-
state.checkLineBreaks = (minor < 2)
|
|
500
|
-
|
|
501
|
-
if (minor !== 1 && minor !== 2) {
|
|
502
|
-
throwWarning(state, 'unsupported YAML version of the document')
|
|
503
|
-
}
|
|
504
493
|
},
|
|
505
494
|
|
|
506
495
|
TAG: function handleTagDirective (state, name, args) {
|
|
@@ -585,8 +574,7 @@ function mergeMappings (state, destination, source, overridableKeys) {
|
|
|
585
574
|
}
|
|
586
575
|
}
|
|
587
576
|
|
|
588
|
-
function storeMappingPair (state, _result, overridableKeys, keyTag, keyNode, valueNode
|
|
589
|
-
startLine, startLineStart, startPos) {
|
|
577
|
+
function storeMappingPair (state, _result, overridableKeys, keyTag, keyNode, valueNode) {
|
|
590
578
|
// The output is a plain object here, so keys can only be strings.
|
|
591
579
|
// We need to convert keyNode to a string, but doing so can hang the process
|
|
592
580
|
// (deeply nested arrays that explode exponentially using aliases).
|
|
@@ -630,15 +618,7 @@ function storeMappingPair (state, _result, overridableKeys, keyTag, keyNode, val
|
|
|
630
618
|
mergeMappings(state, _result, valueNode, overridableKeys)
|
|
631
619
|
}
|
|
632
620
|
} else {
|
|
633
|
-
|
|
634
|
-
!_hasOwnProperty.call(overridableKeys, keyNode) &&
|
|
635
|
-
_hasOwnProperty.call(_result, keyNode)) {
|
|
636
|
-
state.line = startLine || state.line
|
|
637
|
-
state.lineStart = startLineStart || state.lineStart
|
|
638
|
-
state.position = startPos || state.position
|
|
639
|
-
throwError(state, 'duplicated mapping key')
|
|
640
|
-
}
|
|
641
|
-
|
|
621
|
+
// json mode: a repeated key takes the last value.
|
|
642
622
|
setProperty(_result, keyNode, valueNode)
|
|
643
623
|
delete overridableKeys[keyNode]
|
|
644
624
|
}
|
|
@@ -699,10 +679,6 @@ function skipSeparationSpace (state, allowComments, checkIndent) {
|
|
|
699
679
|
}
|
|
700
680
|
}
|
|
701
681
|
|
|
702
|
-
if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) {
|
|
703
|
-
throwWarning(state, 'deficient indentation')
|
|
704
|
-
}
|
|
705
|
-
|
|
706
682
|
return lineBreaks
|
|
707
683
|
}
|
|
708
684
|
|
|
@@ -956,8 +932,6 @@ function readDoubleQuotedScalar (state, nodeIndent) {
|
|
|
956
932
|
function readFlowCollection (state, nodeIndent) {
|
|
957
933
|
let readNext = true
|
|
958
934
|
let _line
|
|
959
|
-
let _lineStart
|
|
960
|
-
let _pos
|
|
961
935
|
const _tag = state.tag
|
|
962
936
|
let _result
|
|
963
937
|
const _anchor = state.anchor
|
|
@@ -1023,8 +997,6 @@ function readFlowCollection (state, nodeIndent) {
|
|
|
1023
997
|
}
|
|
1024
998
|
|
|
1025
999
|
_line = state.line // Save the current line.
|
|
1026
|
-
_lineStart = state.lineStart
|
|
1027
|
-
_pos = state.position
|
|
1028
1000
|
composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true)
|
|
1029
1001
|
keyTag = state.tag
|
|
1030
1002
|
keyNode = state.result
|
|
@@ -1041,9 +1013,9 @@ function readFlowCollection (state, nodeIndent) {
|
|
|
1041
1013
|
}
|
|
1042
1014
|
|
|
1043
1015
|
if (isMapping) {
|
|
1044
|
-
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode
|
|
1016
|
+
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode)
|
|
1045
1017
|
} else if (isPair) {
|
|
1046
|
-
_result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode
|
|
1018
|
+
_result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode))
|
|
1047
1019
|
} else {
|
|
1048
1020
|
_result.push(keyNode)
|
|
1049
1021
|
}
|
|
@@ -1274,9 +1246,6 @@ function readBlockSequence (state, nodeIndent) {
|
|
|
1274
1246
|
|
|
1275
1247
|
function readBlockMapping (state, nodeIndent, flowIndent) {
|
|
1276
1248
|
let allowCompact
|
|
1277
|
-
let _keyLine
|
|
1278
|
-
let _keyLineStart
|
|
1279
|
-
let _keyPos
|
|
1280
1249
|
const _tag = state.tag
|
|
1281
1250
|
const _anchor = state.anchor
|
|
1282
1251
|
const _result = {}
|
|
@@ -1313,7 +1282,7 @@ function readBlockMapping (state, nodeIndent, flowIndent) {
|
|
|
1313
1282
|
if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && isWsOrEol(following)) {
|
|
1314
1283
|
if (ch === 0x3F/* ? */) {
|
|
1315
1284
|
if (atExplicitKey) {
|
|
1316
|
-
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null
|
|
1285
|
+
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null)
|
|
1317
1286
|
keyTag = keyNode = valueNode = null
|
|
1318
1287
|
}
|
|
1319
1288
|
|
|
@@ -1335,10 +1304,6 @@ function readBlockMapping (state, nodeIndent, flowIndent) {
|
|
|
1335
1304
|
// Implicit notation case. Flow-style node as the key first, then ":", and the value.
|
|
1336
1305
|
//
|
|
1337
1306
|
} else {
|
|
1338
|
-
_keyLine = state.line
|
|
1339
|
-
_keyLineStart = state.lineStart
|
|
1340
|
-
_keyPos = state.position
|
|
1341
|
-
|
|
1342
1307
|
if (!composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) {
|
|
1343
1308
|
// Neither implicit nor explicit notation.
|
|
1344
1309
|
// Reading is done. Go to the epilogue.
|
|
@@ -1360,7 +1325,7 @@ function readBlockMapping (state, nodeIndent, flowIndent) {
|
|
|
1360
1325
|
}
|
|
1361
1326
|
|
|
1362
1327
|
if (atExplicitKey) {
|
|
1363
|
-
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null
|
|
1328
|
+
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null)
|
|
1364
1329
|
keyTag = keyNode = valueNode = null
|
|
1365
1330
|
}
|
|
1366
1331
|
|
|
@@ -1389,12 +1354,6 @@ function readBlockMapping (state, nodeIndent, flowIndent) {
|
|
|
1389
1354
|
// Common reading code for both explicit and implicit notations.
|
|
1390
1355
|
//
|
|
1391
1356
|
if (state.line === _line || state.lineIndent > nodeIndent) {
|
|
1392
|
-
if (atExplicitKey) {
|
|
1393
|
-
_keyLine = state.line
|
|
1394
|
-
_keyLineStart = state.lineStart
|
|
1395
|
-
_keyPos = state.position
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
1357
|
if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) {
|
|
1399
1358
|
if (atExplicitKey) {
|
|
1400
1359
|
keyNode = state.result
|
|
@@ -1404,7 +1363,7 @@ function readBlockMapping (state, nodeIndent, flowIndent) {
|
|
|
1404
1363
|
}
|
|
1405
1364
|
|
|
1406
1365
|
if (!atExplicitKey) {
|
|
1407
|
-
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode
|
|
1366
|
+
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode)
|
|
1408
1367
|
keyTag = keyNode = valueNode = null
|
|
1409
1368
|
}
|
|
1410
1369
|
|
|
@@ -1425,7 +1384,7 @@ function readBlockMapping (state, nodeIndent, flowIndent) {
|
|
|
1425
1384
|
|
|
1426
1385
|
// Special case: last mapping's node contains only the key in explicit notation.
|
|
1427
1386
|
if (atExplicitKey) {
|
|
1428
|
-
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null
|
|
1387
|
+
storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null)
|
|
1429
1388
|
}
|
|
1430
1389
|
|
|
1431
1390
|
// Expose the resulting mapping.
|
|
@@ -1807,12 +1766,10 @@ function composeNode (state, parentIndent, nodeContext, allowToSeek, allowCompac
|
|
|
1807
1766
|
}
|
|
1808
1767
|
|
|
1809
1768
|
function readDocument (state) {
|
|
1810
|
-
const documentStart = state.position
|
|
1811
1769
|
let hasDirectives = false
|
|
1812
1770
|
let ch
|
|
1813
1771
|
|
|
1814
1772
|
state.version = null
|
|
1815
|
-
state.checkLineBreaks = state.legacy
|
|
1816
1773
|
state.tagMap = Object.create(null)
|
|
1817
1774
|
state.anchorMap = Object.create(null)
|
|
1818
1775
|
|
|
@@ -1864,10 +1821,9 @@ function readDocument (state) {
|
|
|
1864
1821
|
|
|
1865
1822
|
if (ch !== 0) readLineBreak(state)
|
|
1866
1823
|
|
|
1824
|
+
// An unknown directive is ignored.
|
|
1867
1825
|
if (_hasOwnProperty.call(directiveHandlers, directiveName)) {
|
|
1868
1826
|
directiveHandlers[directiveName](state, directiveName, directiveArgs)
|
|
1869
|
-
} else {
|
|
1870
|
-
throwWarning(state, 'unknown document directive "' + directiveName + '"')
|
|
1871
1827
|
}
|
|
1872
1828
|
}
|
|
1873
1829
|
|
|
@@ -1886,11 +1842,6 @@ function readDocument (state) {
|
|
|
1886
1842
|
composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true)
|
|
1887
1843
|
skipSeparationSpace(state, true, -1)
|
|
1888
1844
|
|
|
1889
|
-
if (state.checkLineBreaks &&
|
|
1890
|
-
PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) {
|
|
1891
|
-
throwWarning(state, 'non-ASCII line breaks are interpreted as content')
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
1845
|
state.documents.push(state.result)
|
|
1895
1846
|
|
|
1896
1847
|
if (state.position === state.lineStart && testDocumentSeparator(state)) {
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* an agent session reloads from, read from the repository alone.
|
|
4
4
|
*
|
|
5
5
|
* A session is declared in the declaration's `agents` list and bound to one
|
|
6
|
-
* member. This prints the session, its
|
|
6
|
+
* member or several (ws-101). This prints the session, its members, its
|
|
7
|
+
* write scope (the files
|
|
7
8
|
* and the record kinds, with their verbs, it may write) and the spec: the
|
|
8
9
|
* `spec` block `chant workspace records --current --json` prints (#2546). An
|
|
9
10
|
* agent that resumes needs nothing else: no memory of an earlier run, no
|
|
@@ -45,6 +46,13 @@ export interface AgentKindScope {
|
|
|
45
46
|
verbs: WriteVerb[];
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/** A member an agent session is bound to. */
|
|
50
|
+
export interface AgentMember {
|
|
51
|
+
name: string;
|
|
52
|
+
dir: string;
|
|
53
|
+
kind: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
interface Head {
|
|
49
57
|
$schema: string;
|
|
50
58
|
contract: number;
|
|
@@ -54,11 +62,18 @@ interface Head {
|
|
|
54
62
|
export type AgentDocument =
|
|
55
63
|
| (Head & {
|
|
56
64
|
workspace: { name: string; root: string; scopeFrom: "base" | "working-tree" };
|
|
57
|
-
agent: {
|
|
65
|
+
agent: {
|
|
66
|
+
name: string;
|
|
67
|
+
/** The first member the session is bound to, kept for readers of a one-member session. */
|
|
68
|
+
member: AgentMember;
|
|
69
|
+
/** Every member the session is bound to, in the order it names them (ws-101). */
|
|
70
|
+
members: AgentMember[];
|
|
71
|
+
principals: string[];
|
|
72
|
+
};
|
|
58
73
|
scope: {
|
|
59
|
-
/** The
|
|
74
|
+
/** The members whose files the session writes. */
|
|
60
75
|
members: string[];
|
|
61
|
-
/** Every declared kind in reach: the workspace's own
|
|
76
|
+
/** Every declared kind in reach: the workspace's own, then each member's. */
|
|
62
77
|
records: AgentKindScope[];
|
|
63
78
|
/** The paths writeScope.agent.protected keeps the session from writing, with the JSON keys each still allows (#3146). */
|
|
64
79
|
protected: { path: string; except: string[] }[];
|
|
@@ -81,13 +96,14 @@ export async function agentSession(q: { cwd: string; name: string }): Promise<Ag
|
|
|
81
96
|
const decl = source.declaration;
|
|
82
97
|
const writer = resolveWriter(decl, source.policy, { agent: q.name }, source.classes);
|
|
83
98
|
const agent = writer.agent!;
|
|
84
|
-
const
|
|
99
|
+
const bound = agent.members.map((name) => decl.members.find((m) => m.name === name)!);
|
|
100
|
+
const members: AgentMember[] = bound.map((m) => ({ name: m.name, dir: m.dir, kind: m.kind }));
|
|
85
101
|
const scope = scopeOf(decl, writer)!;
|
|
86
102
|
const rules = scope.records;
|
|
87
103
|
// The spec query, over every declared kind in the working tree.
|
|
88
104
|
const set = await queryDeclaredRecords(declaredKindFiles(q.cwd), { cwd: q.cwd, current: true });
|
|
89
105
|
const loadedName = new Map(set.kinds.map((k) => [k.declared.path, "error" in k ? null : k.kind.name]));
|
|
90
|
-
const records: AgentKindScope[] = [...decl.records, ...
|
|
106
|
+
const records: AgentKindScope[] = [...decl.records, ...bound.flatMap((m) => m.records)].map((d) => {
|
|
91
107
|
const kind = loadedName.get(d.path) ?? null;
|
|
92
108
|
const names = [kind, d.name].filter((n): n is string => n !== null);
|
|
93
109
|
const verbs = rules === null ? [...WRITE_VERBS] : WRITE_VERBS.filter((v) => names.some((n) => (rules[n] ?? []).includes(v)));
|
|
@@ -96,8 +112,8 @@ export async function agentSession(q: { cwd: string; name: string }): Promise<Ag
|
|
|
96
112
|
return {
|
|
97
113
|
...head,
|
|
98
114
|
workspace: { name: decl.name, root: source.root ?? ".", scopeFrom: source.from },
|
|
99
|
-
agent: { name: agent.name, member:
|
|
100
|
-
scope: { members:
|
|
115
|
+
agent: { name: agent.name, member: members[0], members, principals: agent.principals },
|
|
116
|
+
scope: { members: members.map((m) => m.name), records, protected: scope.protected.map((p) => ({ path: p.path, except: [...p.except] })) },
|
|
101
117
|
spec: set.spec ?? { kinds: [], records: [] },
|
|
102
118
|
reload: [`chant workspace agent ${agent.name} --json`, "chant workspace records --current --json"],
|
|
103
119
|
};
|
|
@@ -127,7 +143,8 @@ export async function runWorkspaceAgent(ctx: CommandContext): Promise<number> {
|
|
|
127
143
|
}
|
|
128
144
|
if (args.json || args.format === "json") return 0;
|
|
129
145
|
const out: string[] = [];
|
|
130
|
-
|
|
146
|
+
const bound = doc.agent.members.map((m) => `${m.name} (${m.dir})`).join(", ");
|
|
147
|
+
out.push(`agent ${doc.agent.name}, bound to ${doc.agent.members.length === 1 ? "member" : "members"} ${bound}, scope read from ${doc.workspace.scopeFrom}`);
|
|
131
148
|
for (const r of doc.scope.records) out.push(`records ${r.name ?? r.kind ?? r.path} (${r.path}): ${r.verbs.length > 0 ? r.verbs.join(", ") : "not writable"}`);
|
|
132
149
|
for (const p of doc.scope.protected) out.push(`protected ${p.path}${p.except.length > 0 ? ` (except ${p.except.join(", ")})` : ""}`);
|
|
133
150
|
out.push(`spec ${doc.spec.records.length} current records of ${doc.spec.kinds.length} spec kinds`);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://intentius.io/chant/schemas/workspace/agent/v1/agent.schema.json",
|
|
4
4
|
"title": "chant workspace agent output",
|
|
5
|
-
"description": "What `chant workspace agent <name> --json` prints (#2524 D20, #2548, ws-067): an agent session, the
|
|
5
|
+
"description": "What `chant workspace agent <name> --json` prints (#2524 D20, #2548, ws-067): an agent session, the members it is bound to (one, or several since ws-101), its write scope and the spec, read from the repository alone, so a session that resumes rebuilds its context from this and nothing else. The session and its scope are read from the declaration at base, as the write paths and check --changes read them; the spec from the working tree. Readers ignore fields they do not know; a field is only ever added within a version. The error codes are a closed list, and every one is in the one closed list of `reason-codes.ts`. Contract version 1; this document was added to it by #2548.",
|
|
6
6
|
"oneOf": [
|
|
7
7
|
{
|
|
8
8
|
"$ref": "#/$defs/result"
|
|
@@ -72,23 +72,16 @@
|
|
|
72
72
|
"type": "string"
|
|
73
73
|
},
|
|
74
74
|
"member": {
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"dir": {
|
|
86
|
-
"type": "string"
|
|
87
|
-
},
|
|
88
|
-
"kind": {
|
|
89
|
-
"type": "string"
|
|
90
|
-
}
|
|
91
|
-
}
|
|
75
|
+
"$ref": "#/$defs/member",
|
|
76
|
+
"description": "The first member the session is bound to, the only one for a session that names one member."
|
|
77
|
+
},
|
|
78
|
+
"members": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"minItems": 1,
|
|
81
|
+
"items": {
|
|
82
|
+
"$ref": "#/$defs/member"
|
|
83
|
+
},
|
|
84
|
+
"description": "Added in contract 1 by #3505. Every member the session is bound to, in the order its declaration names them (ws-101); the first is member."
|
|
92
85
|
},
|
|
93
86
|
"principals": {
|
|
94
87
|
"type": "array",
|
|
@@ -111,11 +104,11 @@
|
|
|
111
104
|
"items": {
|
|
112
105
|
"type": "string"
|
|
113
106
|
},
|
|
114
|
-
"description": "The
|
|
107
|
+
"description": "The members whose files the session writes: every member it is bound to, in the order its declaration names them."
|
|
115
108
|
},
|
|
116
109
|
"records": {
|
|
117
110
|
"type": "array",
|
|
118
|
-
"description": "Every declared record kind in the session's reach: the workspace's own kinds, then
|
|
111
|
+
"description": "Every declared record kind in the session's reach: the workspace's own kinds, then each member's, in the order of members.",
|
|
119
112
|
"items": {
|
|
120
113
|
"type": "object",
|
|
121
114
|
"required": [
|
|
@@ -168,7 +161,7 @@
|
|
|
168
161
|
},
|
|
169
162
|
"protected": {
|
|
170
163
|
"type": "array",
|
|
171
|
-
"description": "Added in contract 1 by #3146. The paths writeScope.agent.protected keeps the session from writing even inside its
|
|
164
|
+
"description": "Added in contract 1 by #3146. The paths writeScope.agent.protected keeps the session from writing even inside its members, each a glob from the workspace root that also covers everything under a directory it matches, with the top-level keys of a JSON file a write may still change in except. Empty when none is listed. check --changes reports a write to one with write-scope-protected.",
|
|
172
165
|
"items": {
|
|
173
166
|
"type": "object",
|
|
174
167
|
"required": [
|
|
@@ -337,6 +330,25 @@
|
|
|
337
330
|
"scope": false
|
|
338
331
|
}
|
|
339
332
|
},
|
|
333
|
+
"member": {
|
|
334
|
+
"type": "object",
|
|
335
|
+
"required": [
|
|
336
|
+
"name",
|
|
337
|
+
"dir",
|
|
338
|
+
"kind"
|
|
339
|
+
],
|
|
340
|
+
"properties": {
|
|
341
|
+
"name": {
|
|
342
|
+
"type": "string"
|
|
343
|
+
},
|
|
344
|
+
"dir": {
|
|
345
|
+
"type": "string"
|
|
346
|
+
},
|
|
347
|
+
"kind": {
|
|
348
|
+
"type": "string"
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
},
|
|
340
352
|
"asset": {
|
|
341
353
|
"type": "object",
|
|
342
354
|
"required": [
|
|
@@ -35,14 +35,16 @@
|
|
|
35
35
|
},
|
|
36
36
|
"declarationReport": {
|
|
37
37
|
"type": "object",
|
|
38
|
-
"description": "POST /api/box/declaration: the capabilities chant workspace status --json lists for the box's block, sent by the box's steward with the box's own token. At most 32 KB. Each report replaces the last.",
|
|
38
|
+
"description": "POST /api/box/declaration: the capabilities chant workspace status --json lists for the box's block, sent by the box's steward with the box's own token, and, optionally, the box's listing and this month's spend (#3508, ws-102). At most 32 KB. Each report replaces the last.",
|
|
39
39
|
"required": ["capabilities"],
|
|
40
40
|
"properties": {
|
|
41
41
|
"capabilities": {
|
|
42
42
|
"type": "array",
|
|
43
43
|
"maxItems": 20,
|
|
44
44
|
"items": { "$ref": "#/$defs/capability" }
|
|
45
|
-
}
|
|
45
|
+
},
|
|
46
|
+
"listing": { "$ref": "#/$defs/listing" },
|
|
47
|
+
"spend": { "$ref": "#/$defs/spend" }
|
|
46
48
|
}
|
|
47
49
|
},
|
|
48
50
|
"declarationKept": {
|
|
@@ -63,9 +65,49 @@
|
|
|
63
65
|
}
|
|
64
66
|
},
|
|
65
67
|
"at": { "type": "string", "minLength": 1, "description": "An ISO 8601 time." },
|
|
66
|
-
"payer": { "$ref": "#/$defs/payer" }
|
|
68
|
+
"payer": { "$ref": "#/$defs/payer" },
|
|
69
|
+
"listing": { "$ref": "#/$defs/listing", "description": "The report's listing, when the broker keeps one." },
|
|
70
|
+
"spend": { "$ref": "#/$defs/spend", "description": "The report's spend, when the broker keeps one." }
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"listing": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"description": "Optional in a report, added within version 1 by #3508 (ws-102). The box block's listing (ws-077) as chant workspace status --json prints it, less the cover: what a broker that lists boxes lists this one under. The bounds are the ones chant workspace box listing set writes with. A broker that lists no boxes ignores it.",
|
|
76
|
+
"required": ["title", "line"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"published": { "type": "boolean", "description": "Whether the box is listed for people other than its owner. Absent means true." },
|
|
79
|
+
"title": { "type": "string", "maxLength": 60, "pattern": "^[^\\u0000-\\u001f\\u007f]*$" },
|
|
80
|
+
"line": { "type": "string", "maxLength": 140, "pattern": "^[^\\u0000-\\u001f\\u007f]*$" }
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"spend": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"description": "Optional in a report, added within version 1 by #3508 (ws-102). What the box's run records (chant workspace runs --json, ws-076) say it spent in one calendar month, by each run's startedAt: the month's totals, and the same per principal the runs worked for. Only USD costs are summed: a run with no USD cost is counted in unpriced, never as zero. spendFromRuns in broker-protocol.ts computes it. A broker that reports no spend ignores it.",
|
|
86
|
+
"required": ["month", "usd", "runs", "unpriced", "byPrincipal"],
|
|
87
|
+
"properties": {
|
|
88
|
+
"month": { "type": "string", "pattern": "^\\d{4}-(0[1-9]|1[0-2])$", "description": "The month, YYYY-MM, in UTC." },
|
|
89
|
+
"usd": { "$ref": "#/$defs/spendUsd" },
|
|
90
|
+
"runs": { "$ref": "#/$defs/spendCount" },
|
|
91
|
+
"unpriced": { "$ref": "#/$defs/spendCount" },
|
|
92
|
+
"byPrincipal": {
|
|
93
|
+
"type": "array",
|
|
94
|
+
"maxItems": 50,
|
|
95
|
+
"description": "Per principal, as the run records' by names them (ws-080's form), at most 50; principal is null for the runs that name none.",
|
|
96
|
+
"items": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"required": ["principal", "usd", "runs", "unpriced"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"principal": { "type": ["string", "null"], "minLength": 1, "maxLength": 200 },
|
|
101
|
+
"usd": { "$ref": "#/$defs/spendUsd" },
|
|
102
|
+
"runs": { "$ref": "#/$defs/spendCount" },
|
|
103
|
+
"unpriced": { "$ref": "#/$defs/spendCount" }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
67
107
|
}
|
|
68
108
|
},
|
|
109
|
+
"spendUsd": { "type": "number", "minimum": 0, "exclusiveMaximum": 1000000000, "description": "The sum of the priced runs' USD costs." },
|
|
110
|
+
"spendCount": { "type": "integer", "minimum": 0, "exclusiveMaximum": 1000000000, "description": "A count of runs: all of them under runs, those with no USD cost under unpriced." },
|
|
69
111
|
"payer": {
|
|
70
112
|
"type": "object",
|
|
71
113
|
"description": "Whose credential pays for the box's model calls (#3474, ws-098), when the broker says. shared: a credential neither the box's owner nor its visitor owns, such as a house or operator key. owner: the box owner's own. visitor: the credential a person brought for this box. principal is the payer in ws-080's form (github:<login>) when the broker knows them, else null. Optional: a broker that does not say leaves it out, and a box treats that as unknown. The same fact rides on each answer from /llm/anthropic and /decide as the chant-payer header.",
|