@dot-agent/compiler 0.10.2 → 0.12.0-alpha.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/index.d.mts CHANGED
@@ -2,15 +2,15 @@ import { A as PackOptions, C as Capability, D as LangId, E as Integrity, F as St
2
2
  import { Node, Tree } from "web-tree-sitter";
3
3
  import JSZip from "jszip";
4
4
  //#region src/parser.d.ts
5
- declare function initBehaviorParser(): Promise<void>;
5
+ export declare function initBehaviorParser(): Promise<void>;
6
6
  interface ParseResult<T> {
7
7
  ok: T | null;
8
8
  diagnostics: ParseDiagnostic[];
9
9
  }
10
- declare function parseBehaviorFile(text: string): ParseResult<BehaviorFile>;
11
- declare function parseDescriptionFile(text: string): ParseResult<DescriptionFile>;
12
- declare function getBehaviorScxml(text: string): string;
13
- declare function initParsers(): Promise<void>;
10
+ export declare function parseBehaviorFile(text: string): ParseResult<BehaviorFile>;
11
+ export declare function parseDescriptionFile(text: string): ParseResult<DescriptionFile>;
12
+ export declare function getBehaviorScxml(text: string): string;
13
+ export declare function initParsers(): Promise<void>;
14
14
  /**
15
15
  * Parse source text and return a tree-sitter Tree.
16
16
  *
@@ -18,12 +18,12 @@ declare function initParsers(): Promise<void>;
18
18
  * @param text - source text to parse
19
19
  * @param previousTree - optional previous tree for incremental parsing (LSP use)
20
20
  */
21
- declare function parse(langId: LangId, text: string, previousTree?: Tree): Promise<Tree>;
21
+ export declare function parse(langId: LangId, text: string, previousTree?: Tree): Promise<Tree>;
22
22
  /** Parse without initializing if parsers are already ready. Returns null if not initialized. */
23
- declare function parseSync(langId: LangId, text: string, previousTree?: Tree): Tree | null;
24
- declare function nodesOfType(tree: Tree, type: string): Node[];
25
- declare function nodeAtOffset(tree: Tree, offset: number): Node | null;
26
- declare function nodeToRange(node: Node): {
23
+ export declare function parseSync(langId: LangId, text: string, previousTree?: Tree): Tree | null;
24
+ export declare function nodesOfType(tree: Tree, type: string): Node[];
25
+ export declare function nodeAtOffset(tree: Tree, offset: number): Node | null;
26
+ export declare function nodeToRange(node: Node): {
27
27
  start: {
28
28
  line: number;
29
29
  character: number;
@@ -33,34 +33,34 @@ declare function nodeToRange(node: Node): {
33
33
  character: number;
34
34
  };
35
35
  };
36
- declare function positionToOffset(text: string, line: number, character: number): number;
37
- declare function getContextNode(tree: Tree, offset: number): Node;
36
+ export declare function positionToOffset(text: string, line: number, character: number): number;
37
+ export declare function getContextNode(tree: Tree, offset: number): Node;
38
38
  //#endregion
39
39
  //#region src/linter.d.ts
40
- declare function lintDescription(text: string, file?: string): Promise<LintMessage[]>;
41
- declare function lintBehavior(text: string, file?: string, docPath?: string, consolidated?: boolean, externalStates?: Set<string>, mergedText?: string): Promise<LintMessage[]>;
42
- declare function createLinter(): Promise<{
40
+ export declare function lintDescription(text: string, file?: string): Promise<LintMessage[]>;
41
+ export declare function lintBehavior(text: string, file?: string, docPath?: string, consolidated?: boolean, externalStates?: Set<string>, mergedText?: string): Promise<LintMessage[]>;
42
+ export declare function createLinter(): Promise<{
43
43
  lintDescription: typeof lintDescription;
44
44
  lintBehavior: typeof lintBehavior;
45
45
  }>;
46
46
  //#endregion
47
47
  //#region src/zip.d.ts
48
- declare function readZip(filePath: string): Promise<JSZip>;
49
- declare function validateZipBomb(filePath: string): Promise<boolean>;
50
- declare function validateMagicBytes(filePath: string): Promise<boolean>;
51
- declare function writeZip(zip: JSZip, outPath: string): Promise<void>;
48
+ export declare function readZip(filePath: string): Promise<JSZip>;
49
+ export declare function validateZipBomb(filePath: string): Promise<boolean>;
50
+ export declare function validateMagicBytes(filePath: string): Promise<boolean>;
51
+ export declare function writeZip(zip: JSZip, outPath: string): Promise<void>;
52
52
  //#endregion
53
53
  //#region src/pack.d.ts
54
54
  interface ConsolidateResult {
55
55
  mergedText: string;
56
56
  mergeSources: string[];
57
57
  }
58
- declare function consolidate(agentRoot: string, entryFile: string): Promise<ConsolidateResult>;
59
- declare function collectFiles(dir: string, descriptionFile: string, mergedBehaviorText: string, mergeSources: string[], personaFile?: string): Promise<Map<string, string>>;
60
- declare function pack(options?: PackOptions): Promise<PackResult>;
58
+ export declare function consolidate(agentRoot: string, entryFile: string): Promise<ConsolidateResult>;
59
+ export declare function collectFiles(dir: string, descriptionFile: string, mergedBehaviorText: string, mergeSources: string[], personaFile?: string): Promise<Map<string, string>>;
60
+ export declare function pack(options?: PackOptions): Promise<PackResult>;
61
61
  //#endregion
62
62
  //#region src/bundle.d.ts
63
- declare function bundleFromDir(dir: string): Promise<AgentBundle>;
63
+ export declare function bundleFromDir(dir: string): Promise<AgentBundle>;
64
64
  //#endregion
65
- export { type AboutMe, type AgentBundle, type AgentDecl, type AgentFiles, type AnnotatedRef, type BehaviorFile, type BuildAboutmeOptions, CONTENT_NAMESPACES, type Capability, type ContentNamespace, type DescriptionFile, type IdParts, type Integrity, type LangId, type LintMessage, type OntologyRef, type PackOptions, type PackResult, type PropertyDecl, type PropertyType, type StateDef, type Statement, type TriggerDecl, type TypeDefinition, aboutmeToJson, buildAboutme, buildId, bundleFromDir, classifyContentPath, collectFiles, consolidate, createLinter, createZip, extractDigest, extractFiles, extractName, getBehaviorScxml, getContextNode, initBehaviorParser, initParsers, isInContentNamespace, lintBehavior, lintDescription, nodeAtOffset, nodeToRange, nodesOfType, pack, parse, parseAboutme, parseBehaviorFile, parseDescriptionFile, parseId, parseSync, positionToOffset, readZip, validateMagicBytes, validateZipBomb, writeZip };
65
+ export { type AboutMe, type AgentBundle, type AgentDecl, type AgentFiles, type AnnotatedRef, type BehaviorFile, type BuildAboutmeOptions, CONTENT_NAMESPACES, type Capability, type ContentNamespace, type DescriptionFile, type IdParts, type Integrity, type LangId, type LintMessage, type OntologyRef, type PackOptions, type PackResult, type PropertyDecl, type PropertyType, type StateDef, type Statement, type TriggerDecl, type TypeDefinition, aboutmeToJson, buildAboutme, buildId, classifyContentPath, createZip, extractDigest, extractFiles, extractName, isInContentNamespace, parseAboutme, parseId };
66
66
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/parser.ts","../src/linter.ts","../src/zip.ts","../src/pack.ts","../src/bundle.ts"],"mappings":";;;;iBAyBsB,sBAAsB;UAM3B,YAAY;EAC3B,IAAI;EACJ,aAAa;;iBAGC,kBAAkB,eAAe,YAAY;iBAI7C,qBAAqB,eAAe,YAAY;iBAIhD,iBAAiB;iBAIX,eAAe;;;;;;;;iBAmBf,MACpB,QAAQ,QACR,cACA,eAAe,OACd,QAAQ;;iBAOK,UAAU,QAAQ,QAAQ,cAAc,eAAe,OAAO;iBAQ9D,YAAY,MAAM,MAAM,eAAe;iBAKvC,aAAa,MAAM,MAAM,iBAAiB;iBAK1C,YAAY,MAAM;EAChC;IAAS;IAAc;;EACvB;IAAO;IAAc;;;iBAQP,iBAAiB,cAAc,cAAc;iBAS7C,eAAe,MAAM,MAAM,iBAAiB;;;iBCtCtC,gBAAgB,cAAc,gBAA6B,QAAQ;iBAqKnE,aACpB,cACA,eACA,kBACA,wBACA,iBAAiB,aACjB,sBACC,QAAQ;iBAgUW,gBAAY;;;;;;iBCniBZ,QAAQ,mBAAmB,QAAQ;iBAKnC,gBAAgB,mBAAmB;iBAOnC,mBAAmB,mBAAmB;iBAMtC,SAAS,KAAK,OAAO,kBAAkB;;;UC6HnD;EACR;EACA;;iBAGoB,YAAY,mBAAmB,oBAAoB,QAAQ;iBAoN3D,aACpB,aACA,yBACA,4BACA,wBACA,uBACC,QAAQ;iBAiFW,KAAK,UAAS,cAAmB,QAAQ;;;iBCjczC,cAAc,cAAc,QAAQ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/parser.ts","../src/linter.ts","../src/zip.ts","../src/pack.ts","../src/bundle.ts"],"mappings":";;;;wBAmBsB,sBAAsB;UAM3B,YAAY;EAC3B,IAAI;EACJ,aAAa;;wBAGC,kBAAkB,eAAe,YAAY;wBAI7C,qBAAqB,eAAe,YAAY;wBAIhD,iBAAiB;wBAIX,eAAe;;;;;;;;wBAmBf,MACpB,QAAQ,QACR,cACA,eAAe,OACd,QAAQ;;wBAOK,UAAU,QAAQ,QAAQ,cAAc,eAAe,OAAO;wBAQ9D,YAAY,MAAM,MAAM,eAAe;wBAKvC,aAAa,MAAM,MAAM,iBAAiB;wBAK1C,YAAY,MAAM;EAChC;IAAS;IAAc;;EACvB;IAAO;IAAc;;;wBAQP,iBAAiB,cAAc,cAAc;wBAS7C,eAAe,MAAM,MAAM,iBAAiB;;;wBCxCtC,gBAAgB,cAAc,gBAA6B,QAAQ;wBAqKnE,aACpB,cACA,eACA,kBACA,wBACA,iBAAiB,aACjB,sBACC,QAAQ;wBAuXW,gBAAY;;;;;;wBC9lBZ,QAAQ,mBAAmB,QAAQ;wBAKnC,gBAAgB,mBAAmB;wBAOnC,mBAAmB,mBAAmB;wBAMtC,SAAS,KAAK,OAAO,kBAAkB;;;UCmInD;EACR;EACA;;wBAGoB,YAAY,mBAAmB,oBAAoB,QAAQ;wBAqN3D,aACpB,aACA,yBACA,4BACA,wBACA,uBACC,QAAQ;wBAkFW,KAAK,UAAS,cAAmB,QAAQ;;;wBCnczC,cAAc,cAAc,QAAQ"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as COMPILER_VERSION, a as extractFiles, c as buildId, d as isValidVersion, f as parseId, g as parseAboutme, h as buildAboutme, i as createZip, l as extractDigest, m as aboutmeToJson, n as classifyContentPath, o as validateMagicBytes$1, p as slugify, r as isInContentNamespace, s as validateZipBomb$1, t as CONTENT_NAMESPACES, u as extractName } from "./namespace-CjeG5-7u.mjs";
1
+ import { _ as COMPILER_VERSION, a as extractFiles, c as buildId, d as isValidVersion, f as parseId, g as parseAboutme, h as buildAboutme, i as createZip, l as extractDigest, m as aboutmeToJson, n as classifyContentPath, o as validateMagicBytes$1, p as slugify, r as isInContentNamespace, s as validateZipBomb$1, t as CONTENT_NAMESPACES, u as extractName } from "./namespace-B8prujsD.mjs";
2
2
  import { createRequire } from "module";
3
3
  import { Language, Parser } from "web-tree-sitter";
4
4
  import { get_graph, init, parse_behavior, parse_description } from "@dot-agent/parser-dsl";
@@ -257,7 +257,8 @@ function collectMergedStates(tree, docDir, states, visited = /* @__PURE__ */ new
257
257
  for (const mergeNode of nodesOfType(tree, "merge_decl")) {
258
258
  const pathNode = mergeNode.childForFieldName("path");
259
259
  if (!pathNode) continue;
260
- const absPath = resolve(docDir, pathNode.text.replace(/^"|"$/g, ""));
260
+ const filename = pathNode.text.replace(/^"|"$/g, "");
261
+ const absPath = resolve(docDir, filename);
261
262
  if (visited.has(absPath)) continue;
262
263
  visited.add(absPath);
263
264
  let mergedText;
@@ -287,6 +288,8 @@ async function lintBehavior(text, file = "agent.behavior", docPath, consolidated
287
288
  const stateName = nameNode?.text ?? "?";
288
289
  const hasInteract = stateNode.descendantsOfType("interact_stmt").length > 0;
289
290
  const hasGoal = stateNode.descendantsOfType("goal_stmt").length > 0;
291
+ const hasGuide = stateNode.descendantsOfType("guide_stmt").length > 0;
292
+ const hasTeach = stateNode.descendantsOfType("teach_stmt").length > 0;
290
293
  const intentHandlers = stateNode.descendantsOfType("intent_handler");
291
294
  if (hasInteract && !hasGoal) {
292
295
  const { line, col } = nodePosition(nameNode ?? stateNode);
@@ -299,17 +302,54 @@ async function lintBehavior(text, file = "agent.behavior", docPath, consolidated
299
302
  message: `'interact' without 'goal'. Add 'goal "..."' before 'interact'.`
300
303
  });
301
304
  }
302
- if (hasGoal && !hasInteract) {
305
+ const presentOrientationNames = [
306
+ hasGoal && "goal",
307
+ hasGuide && "guide",
308
+ hasTeach && "teach"
309
+ ].filter((n) => n !== false);
310
+ if (presentOrientationNames.length > 0 && !hasInteract) {
303
311
  const { line, col } = nodePosition(nameNode ?? stateNode);
312
+ const list = presentOrientationNames.map((n) => `'${n}'`).join(", ");
313
+ const isPlural = presentOrientationNames.length > 1;
304
314
  messages.push({
305
315
  file,
306
316
  line,
307
317
  col,
308
318
  severity: "warning",
309
319
  code: "W012",
310
- message: `'goal' is only valid in an oriented state. Add 'interact' or remove 'goal'.`
320
+ message: `${list} ${isPlural ? "are" : "is"} only valid in an oriented state. Add 'interact' or remove ${isPlural ? "them" : "it"}.`
311
321
  });
312
322
  }
323
+ const bodyStmts = (stateNode.childForFieldName("body")?.children ?? []).filter((n) => n !== null);
324
+ const firstTransitionIndex = bodyStmts.findIndex((n) => n.type === "transition_stmt");
325
+ const interactsBeforeFirstTransition = bodyStmts.filter((n, i) => n.type === "interact_stmt" && (firstTransitionIndex === -1 || i < firstTransitionIndex));
326
+ if (interactsBeforeFirstTransition.length > 1) for (const dup of interactsBeforeFirstTransition.slice(1)) {
327
+ const { line, col } = nodePosition(dup);
328
+ messages.push({
329
+ file,
330
+ line,
331
+ col,
332
+ severity: "warning",
333
+ code: "W017",
334
+ message: `Duplicate 'interact' in state '${stateName}' before any transition. Only one 'interact' is allowed per state before a 'transition to' — a second one, still reachable in the same entry batch, emits a second 'request_interact' effect with no dedupe at the host layer.`
335
+ });
336
+ }
337
+ const leavingIndex = bodyStmts.findIndex((n) => n.type === "transition_stmt" && n.childForFieldName("state")?.text !== stateName);
338
+ if (leavingIndex !== -1) {
339
+ const target = bodyStmts[leavingIndex].childForFieldName("state")?.text ?? "?";
340
+ for (const stmt of bodyStmts.slice(leavingIndex + 1)) {
341
+ if (stmt.type === "intent_handler" || stmt.type === "offtopic_handler" || stmt.type === "comment") continue;
342
+ const { line, col } = nodePosition(stmt);
343
+ messages.push({
344
+ file,
345
+ line,
346
+ col,
347
+ severity: "warning",
348
+ code: "W018",
349
+ message: `Unreachable: state '${stateName}' leaves for '${target}' on entry before this statement runs. Everything after a state-level 'transition to' is skipped, like code after a return — move it before the transition or remove it.`
350
+ });
351
+ }
352
+ }
313
353
  if (hasInteract && intentHandlers.length === 0) {
314
354
  const { line, col } = nodePosition(nameNode ?? stateNode);
315
355
  messages.push({
@@ -585,11 +625,13 @@ async function readZip(filePath) {
585
625
  }
586
626
  async function validateZipBomb(filePath) {
587
627
  const data = await readFile(filePath);
588
- validateZipBomb$1(await JSZip.loadAsync(data), data.length);
628
+ const zip = await JSZip.loadAsync(data);
629
+ validateZipBomb$1(zip, data.length);
589
630
  return true;
590
631
  }
591
632
  async function validateMagicBytes(filePath) {
592
- validateMagicBytes$1(await readFile(filePath));
633
+ const data = await readFile(filePath);
634
+ validateMagicBytes$1(data);
593
635
  return true;
594
636
  }
595
637
  async function writeZip(zip, outPath) {
@@ -731,7 +773,8 @@ async function resolveVersionInteractive(explicit) {
731
773
  function isExternalPath(agentRoot, declaredPath) {
732
774
  if (isAbsolute(declaredPath)) return true;
733
775
  const normRoot = normalize(resolve(agentRoot));
734
- return relative(normRoot, resolve(normRoot, declaredPath)).startsWith("..");
776
+ const resolved = resolve(normRoot, declaredPath);
777
+ return relative(normRoot, resolved).startsWith("..");
735
778
  }
736
779
  async function discoverDescriptionFile(dir, explicit) {
737
780
  if (explicit) try {
@@ -813,7 +856,6 @@ function collectFileRefs(stmts, out) {
813
856
  case "conditional_stmt":
814
857
  collectFileRefs(s.then, out);
815
858
  if (s.else) collectFileRefs(s.else, out);
816
- break;
817
859
  }
818
860
  }
819
861
  function normalizeRefPath(text) {
@@ -865,9 +907,9 @@ async function findOrphanContentFiles(dir, mergedBehaviorText) {
865
907
  file: bundlePath,
866
908
  line: 1,
867
909
  col: 1,
868
- severity: "warning",
869
- code: "W016",
870
- message: `'${kind}' reference '${text}' resolves outside guides/ or knowledge/ — it will be bundled but is unreachable at runtime; move it under knowledge/ (or guides/) and reference it there`
910
+ severity: "error",
911
+ code: "E022",
912
+ message: `'${kind}' reference '${text}' resolves to '${bundlePath}', which is outside guides/ and knowledge/ — those are the only two directories the runtime can serve content from; move '${bundlePath}' under guides/ or knowledge/ and update the '${kind}' statement to reference it there`
871
913
  });
872
914
  }
873
915
  return [...[...await listContentFiles(dir, "guides"), ...await listContentFiles(dir, "knowledge")].filter((path) => !referenced.has(path)).map((path) => ({
@@ -939,11 +981,15 @@ async function pack(options = {}) {
939
981
  const version = await resolveVersionInteractive(options.version);
940
982
  const commit = await resolveCommit(options.commit);
941
983
  const allFiles = await collectFiles(dir, descriptionFileName, mergedText, mergeSources, df.persona ?? void 0);
942
- const warnings = [...lintWarnings, ...await findOrphanContentFiles(dir, mergedText)];
984
+ const contentMessages = await findOrphanContentFiles(dir, mergedText);
985
+ const contentErrors = contentMessages.filter((m) => m.severity === "error");
986
+ if (contentErrors.length > 0) throw new Error(`Lint failed:\n${contentErrors.map((e) => ` ${e.file}:${e.line}:${e.col} ${e.code} ${e.message}`).join("\n")}`);
987
+ const warnings = [...lintWarnings, ...contentMessages.filter((m) => m.severity === "warning")];
943
988
  const contentForHash = Array.from(allFiles.values()).join("");
944
989
  const sha256 = createHash("sha256").update(contentForHash).digest("hex");
990
+ const namespace = df.agent.domain ?? "unknown";
945
991
  const id = buildId({
946
- namespace: df.agent.domain ?? "unknown",
992
+ namespace,
947
993
  name: slugify(df.agent.name),
948
994
  version,
949
995
  digest: version ? commit : void 0