@sponsio/sdk 0.1.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/contracts/capability/credentials.yaml +188 -0
  2. package/contracts/capability/database.yaml +141 -0
  3. package/contracts/capability/filesystem-strict.yaml +101 -0
  4. package/contracts/capability/filesystem.yaml +239 -0
  5. package/contracts/capability/self-modify.yaml +152 -0
  6. package/contracts/capability/shell.yaml +246 -0
  7. package/contracts/capability/subagent.yaml +184 -0
  8. package/contracts/core/llm_safety.yaml +130 -0
  9. package/contracts/core/runaway.yaml +43 -0
  10. package/contracts/core/universal.yaml +34 -0
  11. package/contracts/incident/claude-code-secret-bypass.yaml +197 -0
  12. package/contracts/incident/cursor-railway-wipe.yaml +164 -0
  13. package/contracts/incident/mcp-composition.yaml +418 -0
  14. package/contracts/incident/openclaw.yaml +500 -0
  15. package/contracts/incident/subagent-escape.yaml +177 -0
  16. package/contracts/premium/.gitkeep +7 -0
  17. package/dist/contract.d.ts +59 -0
  18. package/dist/contract.d.ts.map +1 -0
  19. package/dist/contract.js +97 -0
  20. package/dist/contract.js.map +1 -0
  21. package/dist/core/config-loader.d.ts +90 -0
  22. package/dist/core/config-loader.d.ts.map +1 -0
  23. package/dist/core/config-loader.js +530 -0
  24. package/dist/core/config-loader.js.map +1 -0
  25. package/dist/core/evaluator.d.ts +15 -0
  26. package/dist/core/evaluator.d.ts.map +1 -0
  27. package/dist/core/evaluator.js +87 -0
  28. package/dist/core/evaluator.js.map +1 -0
  29. package/dist/core/formula.d.ts +103 -0
  30. package/dist/core/formula.d.ts.map +1 -0
  31. package/dist/core/formula.js +167 -0
  32. package/dist/core/formula.js.map +1 -0
  33. package/dist/core/grounding.d.ts +43 -0
  34. package/dist/core/grounding.d.ts.map +1 -0
  35. package/dist/core/grounding.js +269 -0
  36. package/dist/core/grounding.js.map +1 -0
  37. package/dist/core/nl-parser.d.ts +12 -0
  38. package/dist/core/nl-parser.d.ts.map +1 -0
  39. package/dist/core/nl-parser.js +152 -0
  40. package/dist/core/nl-parser.js.map +1 -0
  41. package/dist/core/pack-loader.d.ts +15 -0
  42. package/dist/core/pack-loader.d.ts.map +1 -0
  43. package/dist/core/pack-loader.js +220 -0
  44. package/dist/core/pack-loader.js.map +1 -0
  45. package/dist/core/parser.d.ts +19 -0
  46. package/dist/core/parser.d.ts.map +1 -0
  47. package/dist/core/parser.js +235 -0
  48. package/dist/core/parser.js.map +1 -0
  49. package/dist/core/pattern-factory.d.ts +38 -0
  50. package/dist/core/pattern-factory.d.ts.map +1 -0
  51. package/dist/core/pattern-factory.js +222 -0
  52. package/dist/core/pattern-factory.js.map +1 -0
  53. package/dist/core/patterns.d.ts +67 -0
  54. package/dist/core/patterns.d.ts.map +1 -0
  55. package/dist/core/patterns.js +585 -0
  56. package/dist/core/patterns.js.map +1 -0
  57. package/dist/core/session-log.d.ts +91 -0
  58. package/dist/core/session-log.d.ts.map +1 -0
  59. package/dist/core/session-log.js +196 -0
  60. package/dist/core/session-log.js.map +1 -0
  61. package/dist/core/spans.d.ts +92 -0
  62. package/dist/core/spans.d.ts.map +1 -0
  63. package/dist/core/spans.js +148 -0
  64. package/dist/core/spans.js.map +1 -0
  65. package/dist/core/sto.d.ts +120 -0
  66. package/dist/core/sto.d.ts.map +1 -0
  67. package/dist/core/sto.js +147 -0
  68. package/dist/core/sto.js.map +1 -0
  69. package/dist/index.d.ts +267 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +540 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/integrations/claude-agent.d.ts +29 -0
  74. package/dist/integrations/claude-agent.d.ts.map +1 -0
  75. package/dist/integrations/claude-agent.js +40 -0
  76. package/dist/integrations/claude-agent.js.map +1 -0
  77. package/dist/integrations/google-adk.d.ts +27 -0
  78. package/dist/integrations/google-adk.d.ts.map +1 -0
  79. package/dist/integrations/google-adk.js +81 -0
  80. package/dist/integrations/google-adk.js.map +1 -0
  81. package/dist/integrations/langchain.d.ts +26 -0
  82. package/dist/integrations/langchain.d.ts.map +1 -0
  83. package/dist/integrations/langchain.js +74 -0
  84. package/dist/integrations/langchain.js.map +1 -0
  85. package/dist/integrations/openai-agents.d.ts +45 -0
  86. package/dist/integrations/openai-agents.d.ts.map +1 -0
  87. package/dist/integrations/openai-agents.js +81 -0
  88. package/dist/integrations/openai-agents.js.map +1 -0
  89. package/dist/integrations/openai.d.ts +24 -0
  90. package/dist/integrations/openai.d.ts.map +1 -0
  91. package/dist/integrations/openai.js +77 -0
  92. package/dist/integrations/openai.js.map +1 -0
  93. package/dist/integrations/vercel-ai.d.ts +38 -0
  94. package/dist/integrations/vercel-ai.d.ts.map +1 -0
  95. package/dist/integrations/vercel-ai.js +107 -0
  96. package/dist/integrations/vercel-ai.js.map +1 -0
  97. package/dist/render/components.d.ts +23 -0
  98. package/dist/render/components.d.ts.map +1 -0
  99. package/dist/render/components.js +133 -0
  100. package/dist/render/components.js.map +1 -0
  101. package/dist/render/derive.d.ts +23 -0
  102. package/dist/render/derive.d.ts.map +1 -0
  103. package/dist/render/derive.js +92 -0
  104. package/dist/render/derive.js.map +1 -0
  105. package/dist/render/session-view.d.ts +19 -0
  106. package/dist/render/session-view.d.ts.map +1 -0
  107. package/dist/render/session-view.js +236 -0
  108. package/dist/render/session-view.js.map +1 -0
  109. package/dist/render/tokens.d.ts +44 -0
  110. package/dist/render/tokens.d.ts.map +1 -0
  111. package/dist/render/tokens.js +72 -0
  112. package/dist/render/tokens.js.map +1 -0
  113. package/package.json +83 -0
@@ -0,0 +1,152 @@
1
+ /**
2
+ * NL → Formula parser (rule-based keyword matching).
3
+ *
4
+ * Simplified port of sponsio/generation/nl_to_contract.py.
5
+ * Handles common NL patterns like:
6
+ * "tool `A` must precede `B`"
7
+ * "tool `X` at most 3 times"
8
+ * "tools `A` and `B` are mutually exclusive"
9
+ */
10
+ import { mustPrecede, alwaysFollowedBy, rateLimit, idempotent, mutualExclusion, noReversal, cooldown, deadline, } from "./patterns.js";
11
+ import { Atom } from "./formula.js";
12
+ /** Extract backtick-wrapped tool names from NL text. */
13
+ function extractTools(text) {
14
+ const matches = text.match(/`([^`]+)`/g);
15
+ if (!matches)
16
+ return [];
17
+ return matches.map((m) => m.replace(/`/g, ""));
18
+ }
19
+ /** Extract a number from text. */
20
+ function extractNumber(text) {
21
+ const m = text.match(/(\d+)/);
22
+ return m ? parseInt(m[1], 10) : null;
23
+ }
24
+ const KEYWORD_RULES = [
25
+ // Deadline (before rate_limit so "within N steps" doesn't get swallowed by
26
+ // any "N times"-style rule). Mirrors the Python ``nl_to_contract`` regex set.
27
+ {
28
+ patterns: [
29
+ /within\s+\d+\s+steps?\s+(?:of|after)/,
30
+ /deadline\s+(?:of\s+)?\d+\s+steps?/,
31
+ /must.*within\s+\d+\s+steps?/,
32
+ ],
33
+ patternName: "deadline",
34
+ minArgs: 2,
35
+ },
36
+ // Rate limit (before idempotent — "at most N times")
37
+ {
38
+ patterns: [/at most.*times/, /maximum.*invocations/, /limit.*calls/, /no more than.*times/],
39
+ patternName: "rate_limit",
40
+ minArgs: 1,
41
+ },
42
+ // Idempotent
43
+ {
44
+ patterns: [/idempotent/, /at most once/, /only (?:once|call(?:ed)? once)/, /single invocation/],
45
+ patternName: "idempotent",
46
+ minArgs: 1,
47
+ },
48
+ // Mutual exclusion
49
+ {
50
+ patterns: [/mutually exclusive/, /exactly one of/, /either.*or.*not both/, /only one of/],
51
+ patternName: "mutual_exclusion",
52
+ minArgs: 2,
53
+ },
54
+ // Always followed by
55
+ {
56
+ patterns: [/(?:must be |always )?followed by/, /must eventually follow/],
57
+ patternName: "always_followed_by",
58
+ minArgs: 2,
59
+ },
60
+ // No reversal
61
+ {
62
+ patterns: [/cannot.*after\s+approv/, /no reversal/, /cannot\s+deny\s+after/, /must\s+not.*after/],
63
+ patternName: "no_reversal",
64
+ minArgs: 2,
65
+ },
66
+ // Cooldown
67
+ {
68
+ patterns: [/cooldown/, /minimum\s+\d+\s+steps?\s+between/],
69
+ patternName: "cooldown",
70
+ minArgs: 1,
71
+ },
72
+ // Must precede (last — most general, requires backtick context)
73
+ {
74
+ patterns: [/precede/, /`[^`]+`\s+(?:must\s+)?before\s+`/, /before\s+`/, /is\s+required\s+before/],
75
+ patternName: "must_precede",
76
+ minArgs: 2,
77
+ },
78
+ ];
79
+ /**
80
+ * Recognise bare "called \`X\`" / "calls \`X\`" / "\`X\` was called"
81
+ * phrasings — Python's parser treats these as standalone ``called(X)``
82
+ * atoms, which is what the ``contract().assume("called \`X\`")``
83
+ * builder snippet leans on. Keeping this as a fallback path (after
84
+ * the richer pattern rules) means a phrase like "tool \`A\` must
85
+ * precede \`B\`" still binds to ``must_precede`` first.
86
+ */
87
+ function parseBareCalledAtom(text, tools) {
88
+ if (tools.length !== 1)
89
+ return null;
90
+ const lower = text.toLowerCase();
91
+ if (/\bcalled\s+`[^`]+`/.test(lower) ||
92
+ /\bcalls\s+`[^`]+`/.test(lower) ||
93
+ /`[^`]+`\s+(?:was|is)\s+called/.test(lower)) {
94
+ return {
95
+ formula: new Atom("called", [tools[0]]),
96
+ desc: `called(${tools[0]})`,
97
+ patternName: "called",
98
+ liveness: false,
99
+ };
100
+ }
101
+ return null;
102
+ }
103
+ export function parseNl(text) {
104
+ const lower = text.toLowerCase();
105
+ const tools = extractTools(text);
106
+ for (const rule of KEYWORD_RULES) {
107
+ const matched = rule.patterns.some((p) => p.test(lower));
108
+ if (!matched)
109
+ continue;
110
+ if (tools.length < rule.minArgs)
111
+ continue;
112
+ switch (rule.patternName) {
113
+ case "must_precede":
114
+ return mustPrecede(tools[0], tools[1]);
115
+ case "always_followed_by":
116
+ return alwaysFollowedBy(tools[0], tools[1]);
117
+ case "rate_limit": {
118
+ const n = extractNumber(text);
119
+ if (n == null)
120
+ continue;
121
+ return rateLimit(tools[0], n);
122
+ }
123
+ case "idempotent":
124
+ return idempotent(tools[0]);
125
+ case "mutual_exclusion":
126
+ return mutualExclusion(tools[0], tools[1]);
127
+ case "no_reversal":
128
+ return noReversal(tools[0], tools[1]);
129
+ case "cooldown": {
130
+ const n = extractNumber(text);
131
+ if (n == null)
132
+ continue;
133
+ return cooldown(tools[0], n);
134
+ }
135
+ case "deadline": {
136
+ const n = extractNumber(text);
137
+ if (n == null)
138
+ continue;
139
+ // Parity with Python NL parser: ``deadline(actions[0], actions[1], n)``,
140
+ // i.e. tools[0] is the trigger and tools[1] is the action that must
141
+ // occur within ``n`` steps. NL phrasings to use: "after `X`, `Y` must
142
+ // occur within N steps".
143
+ return deadline(tools[0], tools[1], n);
144
+ }
145
+ default:
146
+ continue;
147
+ }
148
+ }
149
+ // Fallback: single-atom phrasings used in A/E contract assumptions.
150
+ return parseBareCalledAtom(text, tools);
151
+ }
152
+ //# sourceMappingURL=nl-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nl-parser.js","sourceRoot":"","sources":["../../src/core/nl-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { SkippedItem } from "./config-loader.js";
2
+ export declare function bundledContractsDir(): string | null;
3
+ export declare class IncludeResolveError extends Error {
4
+ }
5
+ export interface PackContractItem {
6
+ raw: unknown;
7
+ packSource: string;
8
+ }
9
+ /**
10
+ * Resolve a single ``include:`` entry to a flat list of contract
11
+ * items (pre-projection). The caller projects each item through the
12
+ * same pipeline as user-yaml contracts.
13
+ */
14
+ export declare function loadPackContracts(spec: string, baseDir: string, seen?: Set<string>, skipped?: SkippedItem[]): PackContractItem[];
15
+ //# sourceMappingURL=pack-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack-loader.d.ts","sourceRoot":"","sources":["../../src/core/pack-loader.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAqBtD,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAmBnD;AAmBD,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AA0DjD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,OAAO,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,EAC7B,OAAO,GAAE,WAAW,EAAO,GAC1B,gBAAgB,EAAE,CAsFpB"}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Resolve and load contract packs referenced via ``include:`` in
3
+ * sponsio.yaml.
4
+ *
5
+ * Mirrors the Python ``sponsio.config._resolve_include_spec`` /
6
+ * ``_load_pack_contracts`` behaviour:
7
+ *
8
+ * - ``sponsio:<category>/<name>`` resolves to the bundled pack
9
+ * yaml under this package's ``contracts/`` directory. Confined
10
+ * to that subtree so ``sponsio:../../etc/passwd`` can't escape.
11
+ * - Bare paths resolve relative to the directory containing the
12
+ * including yaml (or absolute as-is). Confined to the including
13
+ * directory's subtree.
14
+ * - Pack files must declare exactly one agent named ``"*"`` (the
15
+ * template). The pack's contract list is pulled out and stamped
16
+ * with ``packSource = spec`` for downstream attribution.
17
+ * - Nested includes are resolved recursively, with cycle detection.
18
+ *
19
+ * Lives separately from ``config-loader.ts`` so the loader's main
20
+ * path stays focused on user-yaml schema; pack expansion is a
21
+ * recursive sub-pass.
22
+ */
23
+ import { readFileSync, existsSync, readdirSync } from "node:fs";
24
+ import { dirname, isAbsolute, join, resolve, relative } from "node:path";
25
+ import { createRequire } from "node:module";
26
+ import { fileURLToPath } from "node:url";
27
+ const requireCjs = createRequire(import.meta.url);
28
+ function loadYamlLib() {
29
+ return requireCjs("yaml");
30
+ }
31
+ /**
32
+ * Locate the bundled ``contracts/`` directory shipped with this SDK
33
+ * package. In a published install it lives at
34
+ * ``node_modules/@sponsio/sdk/contracts/``; during local development
35
+ * (``pnpm/npm link``, monorepo workspaces) it lives at
36
+ * ``ts/packages/sdk/contracts/``. Walks up from this file's location
37
+ * until it finds the directory.
38
+ */
39
+ let _bundledContractsDir;
40
+ export function bundledContractsDir() {
41
+ if (_bundledContractsDir !== undefined)
42
+ return _bundledContractsDir;
43
+ // Override hook for embedded / non-standard layouts.
44
+ if (process.env.SPONSIO_CONTRACTS_DIR && existsSync(process.env.SPONSIO_CONTRACTS_DIR)) {
45
+ _bundledContractsDir = resolve(process.env.SPONSIO_CONTRACTS_DIR);
46
+ return _bundledContractsDir;
47
+ }
48
+ const here = dirname(fileURLToPath(import.meta.url));
49
+ // dist/core/pack-loader.js → ../../contracts
50
+ // src/core/pack-loader.ts → ../../contracts (during dev)
51
+ const candidates = [join(here, "..", "..", "contracts"), join(here, "..", "contracts")];
52
+ for (const c of candidates) {
53
+ if (existsSync(join(c, "core"))) {
54
+ _bundledContractsDir = resolve(c);
55
+ return _bundledContractsDir;
56
+ }
57
+ }
58
+ _bundledContractsDir = null;
59
+ return null;
60
+ }
61
+ function listAvailablePacks(root) {
62
+ const out = [];
63
+ function walk(dir, prefix) {
64
+ if (!existsSync(dir))
65
+ return;
66
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
67
+ if (entry.isDirectory()) {
68
+ walk(join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
69
+ }
70
+ else if (entry.isFile() && entry.name.endsWith(".yaml")) {
71
+ const stem = entry.name.replace(/\.yaml$/, "");
72
+ out.push(`sponsio:${prefix}/${stem}`);
73
+ }
74
+ }
75
+ }
76
+ walk(root, "");
77
+ return out.sort();
78
+ }
79
+ export class IncludeResolveError extends Error {
80
+ }
81
+ function resolveIncludeSpec(spec, baseDir) {
82
+ if (typeof spec !== "string" || !spec.trim()) {
83
+ throw new IncludeResolveError(`include: entry must be a non-empty string, got ${JSON.stringify(spec)}`);
84
+ }
85
+ if (spec.startsWith("sponsio:")) {
86
+ const rel = spec.slice("sponsio:".length).trim();
87
+ if (!rel) {
88
+ throw new IncludeResolveError(`include: bundled spec is empty: '${spec}' — expected e.g. 'sponsio:core/universal'`);
89
+ }
90
+ const root = bundledContractsDir();
91
+ if (!root) {
92
+ throw new IncludeResolveError(`include: cannot locate bundled contracts/ directory for '${spec}'. ` +
93
+ `Set SPONSIO_CONTRACTS_DIR if running from a non-standard layout.`);
94
+ }
95
+ const filename = rel.endsWith(".yaml") ? rel : rel + ".yaml";
96
+ const candidate = resolve(root, filename);
97
+ // Confine to the bundled tree.
98
+ const relFromRoot = relative(root, candidate);
99
+ if (relFromRoot.startsWith("..") || isAbsolute(relFromRoot)) {
100
+ throw new IncludeResolveError(`include: spec '${spec}' resolves outside the bundled contracts tree`);
101
+ }
102
+ if (!existsSync(candidate)) {
103
+ const available = listAvailablePacks(root);
104
+ throw new IncludeResolveError(`include: bundled pack not found: '${spec}'. Available: ${available.join(", ")}`);
105
+ }
106
+ return candidate;
107
+ }
108
+ // Bare filesystem include.
109
+ let candidate;
110
+ if (isAbsolute(spec)) {
111
+ candidate = resolve(spec);
112
+ }
113
+ else {
114
+ candidate = resolve(baseDir, spec);
115
+ // Confine to baseDir subtree (mirror Python's safe_resolve).
116
+ const relFromBase = relative(baseDir, candidate);
117
+ if (relFromBase.startsWith("..") || isAbsolute(relFromBase)) {
118
+ throw new IncludeResolveError(`include: spec '${spec}' resolves outside the including file's directory (${baseDir}). ` +
119
+ `Use an absolute path or the bundled 'sponsio:...' form for cross-tree includes.`);
120
+ }
121
+ }
122
+ if (!existsSync(candidate)) {
123
+ throw new IncludeResolveError(`include: file not found: ${candidate}`);
124
+ }
125
+ return candidate;
126
+ }
127
+ /**
128
+ * Resolve a single ``include:`` entry to a flat list of contract
129
+ * items (pre-projection). The caller projects each item through the
130
+ * same pipeline as user-yaml contracts.
131
+ */
132
+ export function loadPackContracts(spec, baseDir, seen = new Set(), skipped = []) {
133
+ if (seen.has(spec)) {
134
+ const chain = [...seen, spec].join(" -> ");
135
+ throw new IncludeResolveError(`include: cycle detected: ${chain}`);
136
+ }
137
+ let path;
138
+ try {
139
+ path = resolveIncludeSpec(spec, baseDir);
140
+ }
141
+ catch (e) {
142
+ if (e instanceof IncludeResolveError) {
143
+ skipped.push({ kind: "pack", detail: e.message });
144
+ return [];
145
+ }
146
+ throw e;
147
+ }
148
+ const yamlLib = loadYamlLib();
149
+ let raw;
150
+ try {
151
+ raw = yamlLib.parse(readFileSync(path, "utf-8"));
152
+ }
153
+ catch (e) {
154
+ const msg = e instanceof Error ? e.message : String(e);
155
+ skipped.push({ kind: "pack", detail: `${spec}: invalid yaml in ${path}: ${msg}` });
156
+ return [];
157
+ }
158
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
159
+ skipped.push({ kind: "pack", detail: `${spec}: pack root must be a mapping` });
160
+ return [];
161
+ }
162
+ const agents = raw["agents"];
163
+ if (!agents || typeof agents !== "object" || Array.isArray(agents)) {
164
+ skipped.push({
165
+ kind: "pack",
166
+ detail: `${spec}: pack must define an 'agents:' mapping with a single '*' agent`,
167
+ });
168
+ return [];
169
+ }
170
+ const agentKeys = Object.keys(agents);
171
+ if (agentKeys.length !== 1 || agentKeys[0] !== "*") {
172
+ skipped.push({
173
+ kind: "pack",
174
+ detail: `${spec}: pack must define exactly one agent named '*' (got ${JSON.stringify(agentKeys)})`,
175
+ });
176
+ return [];
177
+ }
178
+ const template = agents["*"];
179
+ if (!template || typeof template !== "object" || Array.isArray(template)) {
180
+ skipped.push({ kind: "pack", detail: `${spec}: '*' agent value must be a mapping` });
181
+ return [];
182
+ }
183
+ const out = [];
184
+ // Recurse into nested includes first (matches Python order).
185
+ const nested = template["include"];
186
+ if (Array.isArray(nested)) {
187
+ seen.add(spec);
188
+ try {
189
+ for (const child of nested) {
190
+ if (typeof child !== "string") {
191
+ skipped.push({
192
+ kind: "pack",
193
+ detail: `${spec}: nested include entry must be a string, got ${typeof child}`,
194
+ });
195
+ continue;
196
+ }
197
+ for (const item of loadPackContracts(child, dirname(path), seen, skipped)) {
198
+ out.push(item);
199
+ }
200
+ }
201
+ }
202
+ finally {
203
+ seen.delete(spec);
204
+ }
205
+ }
206
+ else if (nested !== undefined) {
207
+ skipped.push({ kind: "pack", detail: `${spec}: nested 'include' must be a list of strings` });
208
+ }
209
+ const contracts = template["contracts"];
210
+ if (Array.isArray(contracts)) {
211
+ for (const item of contracts) {
212
+ out.push({ raw: item, packSource: spec });
213
+ }
214
+ }
215
+ else if (contracts !== undefined) {
216
+ skipped.push({ kind: "pack", detail: `${spec}: '*' agent's 'contracts' must be a list` });
217
+ }
218
+ return out;
219
+ }
220
+ //# sourceMappingURL=pack-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack-loader.js","sourceRoot":"","sources":["../../src/core/pack-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Formula repr() ↔ AST parser.
3
+ *
4
+ * Port of sponsio/formulas/parser.py parse_repr().
5
+ * Parses the human-readable repr format back into Formula AST:
6
+ *
7
+ * "G((called('auth') -> F(called('query'))))" → G(Implies(Atom('called', ['auth']), F(Atom('called', ['query']))))
8
+ *
9
+ * Uses recursive descent with tokenizer — the repr uses custom infix
10
+ * operators (!, ->, &, |, U, <=, >=) that aren't valid JS.
11
+ */
12
+ import { Formula } from "./formula.js";
13
+ export declare class ParseError extends Error {
14
+ }
15
+ /**
16
+ * Parse a formula repr string back into an AST.
17
+ */
18
+ export declare function parseRepr(text: string): Formula;
19
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,OAAO,EAKR,MAAM,cAAc,CAAC;AAEtB,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAExC;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAY/C"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Formula repr() ↔ AST parser.
3
+ *
4
+ * Port of sponsio/formulas/parser.py parse_repr().
5
+ * Parses the human-readable repr format back into Formula AST:
6
+ *
7
+ * "G((called('auth') -> F(called('query'))))" → G(Implies(Atom('called', ['auth']), F(Atom('called', ['query']))))
8
+ *
9
+ * Uses recursive descent with tokenizer — the repr uses custom infix
10
+ * operators (!, ->, &, |, U, <=, >=) that aren't valid JS.
11
+ */
12
+ import { Atom, Not, And, Or, Implies, G, F, X, U, Le, Lt, Ge, Gt, Eq, Var, Const, } from "./formula.js";
13
+ export class ParseError extends Error {
14
+ }
15
+ /**
16
+ * Parse a formula repr string back into an AST.
17
+ */
18
+ export function parseRepr(text) {
19
+ const trimmed = text.trim();
20
+ if (!trimmed)
21
+ throw new ParseError("Empty formula");
22
+ const tokens = tokenize(trimmed);
23
+ const state = { pos: 0, tokens };
24
+ const result = parseExpr(state);
25
+ if (state.pos !== tokens.length) {
26
+ throw new ParseError(`Unexpected tokens at position ${state.pos}: ${tokens.slice(state.pos).join(" ")}`);
27
+ }
28
+ return result;
29
+ }
30
+ // --- Tokenizer ---
31
+ function tokenize(text) {
32
+ const tokens = [];
33
+ let i = 0;
34
+ while (i < text.length) {
35
+ const c = text[i];
36
+ if (c === " " || c === "\t" || c === "\n") {
37
+ i++;
38
+ }
39
+ else if (text.slice(i, i + 2) === "->") {
40
+ tokens.push("->");
41
+ i += 2;
42
+ }
43
+ else if (text.slice(i, i + 2) === "<=") {
44
+ tokens.push("<=");
45
+ i += 2;
46
+ }
47
+ else if (text.slice(i, i + 2) === ">=") {
48
+ tokens.push(">=");
49
+ i += 2;
50
+ }
51
+ else if (text.slice(i, i + 2) === "==") {
52
+ tokens.push("==");
53
+ i += 2;
54
+ }
55
+ else if ("()<>&|!,".includes(c)) {
56
+ tokens.push(c);
57
+ i++;
58
+ }
59
+ else if (c === "'" || c === '"') {
60
+ // Quoted string — include quotes
61
+ const q = c;
62
+ let j = i + 1;
63
+ while (j < text.length && text[j] !== q) {
64
+ if (text[j] === "\\")
65
+ j++;
66
+ j++;
67
+ }
68
+ tokens.push(text.slice(i, j + 1));
69
+ i = j + 1;
70
+ }
71
+ else if (c >= "0" && c <= "9" || (c === "-" && i + 1 < text.length && text[i + 1] >= "0" && text[i + 1] <= "9")) {
72
+ let j = c === "-" ? i + 1 : i;
73
+ while (j < text.length && ((text[j] >= "0" && text[j] <= "9") || text[j] === "."))
74
+ j++;
75
+ tokens.push(text.slice(i, j));
76
+ i = j;
77
+ }
78
+ else if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c === "_") {
79
+ let j = i;
80
+ while (j < text.length && ((text[j] >= "a" && text[j] <= "z") ||
81
+ (text[j] >= "A" && text[j] <= "Z") ||
82
+ (text[j] >= "0" && text[j] <= "9") ||
83
+ text[j] === "_"))
84
+ j++;
85
+ tokens.push(text.slice(i, j));
86
+ i = j;
87
+ }
88
+ else {
89
+ i++;
90
+ }
91
+ }
92
+ return tokens;
93
+ }
94
+ function peek(s) {
95
+ return s.tokens[s.pos];
96
+ }
97
+ function consume(s, expected) {
98
+ if (s.pos >= s.tokens.length) {
99
+ throw new ParseError(`Unexpected end, expected ${expected ?? "token"}`);
100
+ }
101
+ const t = s.tokens[s.pos];
102
+ if (expected != null && t !== expected) {
103
+ throw new ParseError(`Expected ${expected} at position ${s.pos}, got ${t}`);
104
+ }
105
+ s.pos++;
106
+ return t;
107
+ }
108
+ function parseExpr(s) {
109
+ let left = parseUnary(s);
110
+ while (true) {
111
+ const op = peek(s);
112
+ if (op === "->" || op === "&" || op === "|" || op === "U" ||
113
+ op === "<=" || op === ">=" || op === "<" || op === ">" || op === "==") {
114
+ consume(s);
115
+ const right = parseUnary(s);
116
+ switch (op) {
117
+ case "->":
118
+ left = new Implies(left, right);
119
+ break;
120
+ case "&":
121
+ left = new And(left, right);
122
+ break;
123
+ case "|":
124
+ left = new Or(left, right);
125
+ break;
126
+ case "U":
127
+ left = new U(left, right);
128
+ break;
129
+ case "<=":
130
+ left = new Le(left, right);
131
+ break;
132
+ case ">=":
133
+ left = new Ge(left, right);
134
+ break;
135
+ case "<":
136
+ left = new Lt(left, right);
137
+ break;
138
+ case ">":
139
+ left = new Gt(left, right);
140
+ break;
141
+ case "==":
142
+ left = new Eq(left, right);
143
+ break;
144
+ }
145
+ }
146
+ else {
147
+ break;
148
+ }
149
+ }
150
+ return left;
151
+ }
152
+ function parseUnary(s) {
153
+ const t = peek(s);
154
+ if (t === "!") {
155
+ consume(s, "!");
156
+ // Accept both ``!(expr)`` and the bare ``!foo`` / ``!foo(a)`` /
157
+ // ``!Atom(...)`` forms. LTL-style yaml in the README writes
158
+ // ``!called(git_commit)`` without explicit parens — without
159
+ // this loosening the loader has to pre-wrap every negation.
160
+ const child = parseUnary(s);
161
+ return new Not(child);
162
+ }
163
+ else if (t === "(") {
164
+ consume(s, "(");
165
+ const expr = parseExpr(s);
166
+ consume(s, ")");
167
+ return expr;
168
+ }
169
+ else if (t === "G" || t === "F" || t === "X") {
170
+ consume(s);
171
+ consume(s, "(");
172
+ const child = parseExpr(s);
173
+ consume(s, ")");
174
+ return t === "G" ? new G(child) : t === "F" ? new F(child) : new X(child);
175
+ }
176
+ else if (t === "Var") {
177
+ consume(s, "Var");
178
+ consume(s, "(");
179
+ const args = [];
180
+ while (peek(s) !== ")") {
181
+ if (peek(s) === ",")
182
+ consume(s, ",");
183
+ args.push(stripQuotes(consume(s)));
184
+ }
185
+ consume(s, ")");
186
+ return new Var(args[0], ...args.slice(1));
187
+ }
188
+ else if (t === "Atom") {
189
+ consume(s, "Atom");
190
+ consume(s, "(");
191
+ const args = [];
192
+ while (peek(s) !== ")") {
193
+ if (peek(s) === ",")
194
+ consume(s, ",");
195
+ args.push(stripQuotes(consume(s)));
196
+ }
197
+ consume(s, ")");
198
+ return new Atom(args[0], args.slice(1));
199
+ }
200
+ else if (t === "Not") {
201
+ consume(s, "Not");
202
+ consume(s, "(");
203
+ const child = parseExpr(s);
204
+ consume(s, ")");
205
+ return new Not(child);
206
+ }
207
+ else if (t && (/^[-0-9.]/.test(t))) {
208
+ consume(s);
209
+ return new Const(parseFloat(t));
210
+ }
211
+ else if (t) {
212
+ // Atom shorthand: predicate_name('arg1', 'arg2', ...)
213
+ const name = consume(s);
214
+ if (peek(s) === "(") {
215
+ consume(s, "(");
216
+ const args = [];
217
+ while (peek(s) !== ")") {
218
+ if (peek(s) === ",")
219
+ consume(s, ",");
220
+ args.push(stripQuotes(consume(s)));
221
+ }
222
+ consume(s, ")");
223
+ return new Atom(name, args);
224
+ }
225
+ return new Atom(name);
226
+ }
227
+ throw new ParseError(`Unexpected token: ${t}`);
228
+ }
229
+ function stripQuotes(s) {
230
+ if (s.length >= 2 && (s[0] === "'" || s[0] === '"')) {
231
+ return s.slice(1, -1);
232
+ }
233
+ return s;
234
+ }
235
+ //# sourceMappingURL=parser.js.map