@sublang/slc 0.1.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.
Files changed (137) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +192 -0
  3. package/dist/app.d.ts +85 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +233 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/artifacts.d.ts +61 -0
  8. package/dist/artifacts.d.ts.map +1 -0
  9. package/dist/artifacts.js +97 -0
  10. package/dist/artifacts.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +26 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/cligent-agent.d.ts +11 -0
  16. package/dist/cligent-agent.d.ts.map +1 -0
  17. package/dist/cligent-agent.js +66 -0
  18. package/dist/cligent-agent.js.map +1 -0
  19. package/dist/compiled-executor.d.ts +42 -0
  20. package/dist/compiled-executor.d.ts.map +1 -0
  21. package/dist/compiled-executor.js +433 -0
  22. package/dist/compiled-executor.js.map +1 -0
  23. package/dist/config-file.d.ts +52 -0
  24. package/dist/config-file.d.ts.map +1 -0
  25. package/dist/config-file.js +159 -0
  26. package/dist/config-file.js.map +1 -0
  27. package/dist/config.d.ts +82 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +161 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/entry-module.d.ts +22 -0
  32. package/dist/entry-module.d.ts.map +1 -0
  33. package/dist/entry-module.js +216 -0
  34. package/dist/entry-module.js.map +1 -0
  35. package/dist/execution.d.ts +71 -0
  36. package/dist/execution.d.ts.map +1 -0
  37. package/dist/execution.js +207 -0
  38. package/dist/execution.js.map +1 -0
  39. package/dist/file-capability.d.ts +46 -0
  40. package/dist/file-capability.d.ts.map +1 -0
  41. package/dist/file-capability.js +234 -0
  42. package/dist/file-capability.js.map +1 -0
  43. package/dist/file-grants.d.ts +79 -0
  44. package/dist/file-grants.d.ts.map +1 -0
  45. package/dist/file-grants.js +133 -0
  46. package/dist/file-grants.js.map +1 -0
  47. package/dist/hash.d.ts +14 -0
  48. package/dist/hash.d.ts.map +1 -0
  49. package/dist/hash.js +34 -0
  50. package/dist/hash.js.map +1 -0
  51. package/dist/index.d.ts +20 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +22 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/interpreter.d.ts +48 -0
  56. package/dist/interpreter.d.ts.map +1 -0
  57. package/dist/interpreter.js +118 -0
  58. package/dist/interpreter.js.map +1 -0
  59. package/dist/invocation.d.ts +64 -0
  60. package/dist/invocation.d.ts.map +1 -0
  61. package/dist/invocation.js +148 -0
  62. package/dist/invocation.js.map +1 -0
  63. package/dist/link.d.ts +63 -0
  64. package/dist/link.d.ts.map +1 -0
  65. package/dist/link.js +172 -0
  66. package/dist/link.js.map +1 -0
  67. package/dist/markdown.d.ts +12 -0
  68. package/dist/markdown.d.ts.map +1 -0
  69. package/dist/markdown.js +63 -0
  70. package/dist/markdown.js.map +1 -0
  71. package/dist/normalize.md +68 -0
  72. package/dist/phase-runner.d.ts +64 -0
  73. package/dist/phase-runner.d.ts.map +1 -0
  74. package/dist/phase-runner.js +322 -0
  75. package/dist/phase-runner.js.map +1 -0
  76. package/dist/phase.d.ts +62 -0
  77. package/dist/phase.d.ts.map +1 -0
  78. package/dist/phase.js +111 -0
  79. package/dist/phase.js.map +1 -0
  80. package/dist/pin-closure.d.ts +19 -0
  81. package/dist/pin-closure.d.ts.map +1 -0
  82. package/dist/pin-closure.js +110 -0
  83. package/dist/pin-closure.js.map +1 -0
  84. package/dist/pin-currency.d.ts +55 -0
  85. package/dist/pin-currency.d.ts.map +1 -0
  86. package/dist/pin-currency.js +435 -0
  87. package/dist/pin-currency.js.map +1 -0
  88. package/dist/pin-generate.d.ts +46 -0
  89. package/dist/pin-generate.d.ts.map +1 -0
  90. package/dist/pin-generate.js +131 -0
  91. package/dist/pin-generate.js.map +1 -0
  92. package/dist/pin-paths.d.ts +11 -0
  93. package/dist/pin-paths.d.ts.map +1 -0
  94. package/dist/pin-paths.js +108 -0
  95. package/dist/pin-paths.js.map +1 -0
  96. package/dist/pins.d.ts +84 -0
  97. package/dist/pins.d.ts.map +1 -0
  98. package/dist/pins.js +265 -0
  99. package/dist/pins.js.map +1 -0
  100. package/dist/pipeline.d.ts +61 -0
  101. package/dist/pipeline.d.ts.map +1 -0
  102. package/dist/pipeline.js +147 -0
  103. package/dist/pipeline.js.map +1 -0
  104. package/dist/playbook-contract.d.ts +194 -0
  105. package/dist/playbook-contract.d.ts.map +1 -0
  106. package/dist/playbook-contract.js +180 -0
  107. package/dist/playbook-contract.js.map +1 -0
  108. package/dist/playbook-ports.d.ts +53 -0
  109. package/dist/playbook-ports.d.ts.map +1 -0
  110. package/dist/playbook-ports.js +217 -0
  111. package/dist/playbook-ports.js.map +1 -0
  112. package/dist/resolver.d.ts +68 -0
  113. package/dist/resolver.d.ts.map +1 -0
  114. package/dist/resolver.js +133 -0
  115. package/dist/resolver.js.map +1 -0
  116. package/dist/runner.d.ts +45 -0
  117. package/dist/runner.d.ts.map +1 -0
  118. package/dist/runner.js +506 -0
  119. package/dist/runner.js.map +1 -0
  120. package/dist/runtime-package.d.ts +7 -0
  121. package/dist/runtime-package.d.ts.map +1 -0
  122. package/dist/runtime-package.js +132 -0
  123. package/dist/runtime-package.js.map +1 -0
  124. package/dist/slc.config.template.yaml +5 -0
  125. package/dist/verify-coverage.d.ts +99 -0
  126. package/dist/verify-coverage.d.ts.map +1 -0
  127. package/dist/verify-coverage.js +1949 -0
  128. package/dist/verify-coverage.js.map +1 -0
  129. package/dist/verify-support.d.ts +10 -0
  130. package/dist/verify-support.d.ts.map +1 -0
  131. package/dist/verify-support.js +52 -0
  132. package/dist/verify-support.js.map +1 -0
  133. package/dist/verify.d.ts +393 -0
  134. package/dist/verify.d.ts.map +1 -0
  135. package/dist/verify.js +1790 -0
  136. package/dist/verify.js.map +1 -0
  137. package/package.json +76 -0
@@ -0,0 +1,133 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ /**
4
+ * Host-side per-run grant model for the file capability (FCAP-11..14; DR-008).
5
+ *
6
+ * A compiled phase artifact reaches files only through a {@link FileCapability}
7
+ * the host builds for that run. The capability is default-deny: an operation
8
+ * succeeds only when its path is inside the run root (enforced by the artifact-
9
+ * facing layer in `file-capability.ts`) and is covered by a {@link Grant} of the
10
+ * matching access. {@link buildRunGrants} encodes the policy — the only writable
11
+ * path is the run's `target` (compile) or `linked` (link), and read grants are
12
+ * the run's source/object inputs, its link target when applicable, and the
13
+ * recorded semantic-input closure, each enumerated as a file grant.
14
+ * {@link createGuardedCapability} wraps a run-root capability with that grant set,
15
+ * reporting `unauthorized` for any ungranted access.
16
+ *
17
+ * Directory listing and recursive directory reads are withheld: until a pinning
18
+ * extension defines directory listing and subtree identities, this model grants
19
+ * neither (DR-008), so a pinned phase cannot widen its closure at run time.
20
+ * Scope failures (`invalid_path`, `unauthorized`) are distinguished by
21
+ * {@link isScopeFailure} so the executor can fail them like a generic check rather
22
+ * than a phase `BLOCKED`. See specs/dev/file-capability.md.
23
+ */
24
+ import { canonicalize, createFileCapability, } from './file-capability.js';
25
+ import {} from './hash.js';
26
+ /**
27
+ * Builds the default-deny grant set for a run (FCAP-12, FCAP-13).
28
+ *
29
+ * The only writable grant is the run's `target` (compile) or `linked` (link); read
30
+ * grants enumerate the source/object inputs, the link target when present, and the
31
+ * semantic-input closure. Directory listing and recursive reads are not granted.
32
+ */
33
+ export function buildRunGrants(spec) {
34
+ const grants = [];
35
+ if (spec.kind === 'compile' && spec.target !== undefined) {
36
+ grants.push(fileGrant(spec.target, 'write', 'target'));
37
+ }
38
+ if (spec.kind === 'link' && spec.linked !== undefined) {
39
+ grants.push(fileGrant(spec.linked, 'write', 'linked'));
40
+ }
41
+ if (spec.source !== undefined) {
42
+ grants.push(fileGrant(spec.source, 'read', 'source'));
43
+ }
44
+ for (const object of spec.objects ?? []) {
45
+ grants.push(fileGrant(object, 'read', 'object'));
46
+ }
47
+ if (spec.linkTarget !== undefined) {
48
+ grants.push(fileGrant(spec.linkTarget, 'read', 'linkTarget'));
49
+ }
50
+ for (const input of spec.semanticInputs ?? []) {
51
+ grants.push(fileGrant(input.path, 'read', 'semanticInput', input.identity));
52
+ }
53
+ return grants;
54
+ }
55
+ /**
56
+ * Wraps a run-root capability with a grant set, denying every ungranted access
57
+ * (FCAP-11). Path validity and run-root containment are left to the wrapped
58
+ * capability, so an invalid or escaping path still reports `invalid_path`.
59
+ */
60
+ export function createGuardedCapability(runRoot, grants) {
61
+ const inner = createFileCapability(runRoot);
62
+ return {
63
+ async read(path) {
64
+ return authorize(grants, path, 'read') ?? inner.read(path);
65
+ },
66
+ async list(path) {
67
+ return authorizeList(grants, path) ?? inner.list(path);
68
+ },
69
+ async write(path, bytes, opts) {
70
+ return authorize(grants, path, 'write') ?? inner.write(path, bytes, opts);
71
+ },
72
+ };
73
+ }
74
+ /** Reports whether a capability error is a host-enforced scope failure (FCAP-14). */
75
+ export function isScopeFailure(code) {
76
+ return code === 'invalid_path' || code === 'unauthorized';
77
+ }
78
+ /**
79
+ * Returns an `unauthorized` denial when no grant covers `path` for `access`, or
80
+ * `null` to defer to the wrapped capability (which validates the path itself).
81
+ */
82
+ function authorize(grants, path, access) {
83
+ const canonical = canonicalize(path);
84
+ if (canonical === null) {
85
+ return null;
86
+ }
87
+ if (!covers(grants, canonical, access)) {
88
+ return deny(`no ${access} grant covers "${path}"`);
89
+ }
90
+ return null;
91
+ }
92
+ function authorizeList(grants, path) {
93
+ const canonical = canonicalize(path);
94
+ if (canonical === null) {
95
+ return null;
96
+ }
97
+ const granted = grants.some((grant) => grant.access === 'read' &&
98
+ grant.path === canonical &&
99
+ grant.kind === 'directory' &&
100
+ grant.listing === true);
101
+ return granted ? null : deny(`no listing grant covers "${path}"`);
102
+ }
103
+ function covers(grants, canonical, access) {
104
+ return grants.some((grant) => {
105
+ if (grant.access !== access) {
106
+ return false;
107
+ }
108
+ if (grant.path === canonical) {
109
+ return true;
110
+ }
111
+ return (access === 'read' &&
112
+ grant.kind === 'directory' &&
113
+ grant.recursive === true &&
114
+ isUnder(grant.path, canonical));
115
+ });
116
+ }
117
+ function isUnder(ancestor, candidate) {
118
+ const prefix = ancestor === '/' ? '/' : `${ancestor}/`;
119
+ return candidate.startsWith(prefix);
120
+ }
121
+ function fileGrant(path, access, reason, identity) {
122
+ const canonical = canonicalize(path);
123
+ if (canonical === null) {
124
+ throw new Error(`grant path "${path}" is not a valid run-root path`);
125
+ }
126
+ return identity === undefined
127
+ ? { path: canonical, access, kind: 'file', reason }
128
+ : { path: canonical, access, kind: 'file', identity, reason };
129
+ }
130
+ function deny(diagnostic) {
131
+ return { ok: false, code: 'unauthorized', diagnostic };
132
+ }
133
+ //# sourceMappingURL=file-grants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-grants.js","sourceRoot":"","sources":["../src/file-grants.ts"],"names":[],"mappings":"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"}
package/dist/hash.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /** A SHA-256 content hash as `sha256:<64 lowercase hex>` (DR-007). */
2
+ export type Hash = `sha256:${string}`;
3
+ /** Hashes exact bytes as `sha256:<64 lowercase hex>`, applying no normalization (DR-007). */
4
+ export declare function hashBytes(bytes: Uint8Array): Hash;
5
+ /**
6
+ * Reads a file and hashes its exact bytes (DR-007).
7
+ *
8
+ * Reads with no encoding so the bytes are hashed verbatim, without line-ending
9
+ * or text transformation.
10
+ */
11
+ export declare function hashFile(path: string): Promise<Hash>;
12
+ /** Reports whether a string is a well-formed `sha256:<64 lowercase hex>` hash (DR-007). */
13
+ export declare function isHash(value: string): value is Hash;
14
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAkBA,sEAAsE;AACtE,MAAM,MAAM,IAAI,GAAG,UAAU,MAAM,EAAE,CAAC;AAItC,6FAA6F;AAC7F,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAEjD;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;AAED,2FAA2F;AAC3F,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,IAAI,CAEnD"}
package/dist/hash.js ADDED
@@ -0,0 +1,34 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ /**
4
+ * Exact-byte SHA-256 hashing for pin currency (PIN-2, PIN-3; DR-007).
5
+ *
6
+ * Hashes are SHA-256 over the exact file bytes — with no line-ending or other
7
+ * text normalization — written as `sha256:` followed by 64 lowercase hexadecimal
8
+ * characters. This is the conservative identity the pin validator compares
9
+ * recorded hashes against, so any byte difference (including a line-ending
10
+ * change) yields a different hash. The DR-003 write-scope snapshot in
11
+ * `execution.ts` keeps its own raw-hex helper; this module owns the pin format.
12
+ * See specs/dev/pinning.md.
13
+ */
14
+ import { createHash } from 'node:crypto';
15
+ import { readFile } from 'node:fs/promises';
16
+ const HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
17
+ /** Hashes exact bytes as `sha256:<64 lowercase hex>`, applying no normalization (DR-007). */
18
+ export function hashBytes(bytes) {
19
+ return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
20
+ }
21
+ /**
22
+ * Reads a file and hashes its exact bytes (DR-007).
23
+ *
24
+ * Reads with no encoding so the bytes are hashed verbatim, without line-ending
25
+ * or text transformation.
26
+ */
27
+ export async function hashFile(path) {
28
+ return hashBytes(await readFile(path));
29
+ }
30
+ /** Reports whether a string is a well-formed `sha256:<64 lowercase hex>` hash (DR-007). */
31
+ export function isHash(value) {
32
+ return HASH_PATTERN.test(value);
33
+ }
34
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,0EAA0E;AAE1E;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAK5C,MAAM,YAAY,GAAG,uBAAuB,CAAC;AAE7C,6FAA6F;AAC7F,MAAM,UAAU,SAAS,CAAC,KAAiB;IACzC,OAAO,UAAU,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACtE,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,OAAO,SAAS,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Public entry point for the SubLang Compiler (slc).
3
+ *
4
+ * The `slc` bin orchestrator (`run`, plus `name`/`version`) lives in app.ts; the
5
+ * generic mechanics, execution boundary, interpreted executor, resolver, and
6
+ * configuration are re-exported from their modules.
7
+ */
8
+ export * from './app.js';
9
+ export * from './artifacts.js';
10
+ export * from './cligent-agent.js';
11
+ export * from './config.js';
12
+ export * from './execution.js';
13
+ export * from './interpreter.js';
14
+ export * from './invocation.js';
15
+ export * from './link.js';
16
+ export * from './phase.js';
17
+ export * from './pipeline.js';
18
+ export * from './resolver.js';
19
+ export * from './runner.js';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ /**
4
+ * Public entry point for the SubLang Compiler (slc).
5
+ *
6
+ * The `slc` bin orchestrator (`run`, plus `name`/`version`) lives in app.ts; the
7
+ * generic mechanics, execution boundary, interpreted executor, resolver, and
8
+ * configuration are re-exported from their modules.
9
+ */
10
+ export * from './app.js';
11
+ export * from './artifacts.js';
12
+ export * from './cligent-agent.js';
13
+ export * from './config.js';
14
+ export * from './execution.js';
15
+ export * from './interpreter.js';
16
+ export * from './invocation.js';
17
+ export * from './link.js';
18
+ export * from './phase.js';
19
+ export * from './pipeline.js';
20
+ export * from './resolver.js';
21
+ export * from './runner.js';
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,0EAA0E;AAE1E;;;;;;GAMG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,48 @@
1
+ import type { ExecuteRequest, PhaseExecutor } from './execution.js';
2
+ /** A single agent invocation: a prompt plus per-run configuration. */
3
+ export interface AgentRunRequest {
4
+ prompt: string;
5
+ cwd?: string;
6
+ model?: string;
7
+ /** Explicit backend continuation selection for Playbook player calls. */
8
+ resume?: string | false;
9
+ /** Explicit tool allowlist for isolated Playbook Captain control calls. */
10
+ allowedTools?: readonly string[];
11
+ signal: AbortSignal;
12
+ }
13
+ /** The normalized outcome of one agent invocation. */
14
+ export interface AgentRunResult {
15
+ /** `success` when the agent finished cleanly; otherwise it did not. */
16
+ status: 'success' | 'error' | 'incomplete';
17
+ /** The agent's final textual report (its summary, or `BLOCKED:` lines). */
18
+ text: string;
19
+ /** Opaque backend continuation token, when the transport exposes one. */
20
+ resumeToken?: string;
21
+ }
22
+ /** The transport to a coding agent, backed by Cligent in production. */
23
+ export interface AgentClient {
24
+ run(request: AgentRunRequest): Promise<AgentRunResult>;
25
+ }
26
+ /** Agent/model selection, applied as configuration not phase semantics (PHEXEC-13). */
27
+ export interface InterpreterConfig {
28
+ model?: string;
29
+ cwd?: string;
30
+ }
31
+ /**
32
+ * Builds the agent-contract prompt for a phase from its definition and inputs
33
+ * (PHEXEC-11, PHEXEC-14, PHEXEC-15).
34
+ */
35
+ export declare function buildPhasePrompt(opts: {
36
+ request: ExecuteRequest;
37
+ definition: string;
38
+ }): string;
39
+ /**
40
+ * Creates a {@link PhaseExecutor} that interprets a phase via one agent
41
+ * invocation (DR-004). Plugs into the DR-003 boundary (`runPhase`), which
42
+ * applies the generic checks to whatever the agent wrote.
43
+ */
44
+ export declare function createInterpretedExecutor(opts: {
45
+ agent: AgentClient;
46
+ config?: InterpreterConfig;
47
+ }): PhaseExecutor;
48
+ //# sourceMappingURL=interpreter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interpreter.d.ts","sourceRoot":"","sources":["../src/interpreter.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,cAAc,EAGd,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB,sEAAsE;AACtE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,YAAY,CAAC;IAC3C,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACxD;AAED,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CA0CT;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B,GAAG,aAAa,CAoChB"}
@@ -0,0 +1,118 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ /**
4
+ * Interpreted phase execution (DR-004).
5
+ *
6
+ * `createInterpretedExecutor` implements the {@link PhaseExecutor} boundary by
7
+ * prompting a coding agent once per phase (PHEXEC-12) with the phase or link
8
+ * definition and the phase inputs (PHEXEC-11), establishing the agent contract
9
+ * (PHEXEC-14) and allowing the agent to run definition-called tools and read
10
+ * cited content (PHEXEC-15). The agent is reached through an injected
11
+ * {@link AgentClient}; the Cligent-backed default lives in cligent-agent.ts so
12
+ * this core stays transport-free and testable. Agent and model selection are
13
+ * configuration, not phase semantics (PHEXEC-13). See
14
+ * specs/dev/phase-execution.md.
15
+ */
16
+ import { readFile } from 'node:fs/promises';
17
+ /**
18
+ * Builds the agent-contract prompt for a phase from its definition and inputs
19
+ * (PHEXEC-11, PHEXEC-14, PHEXEC-15).
20
+ */
21
+ export function buildPhasePrompt(opts) {
22
+ const { request, definition } = opts;
23
+ const target = request.kind === 'compile' ? request.target : request.linked;
24
+ const inputs = request.kind === 'compile'
25
+ ? [
26
+ `source to read: ${request.source}`,
27
+ ...(request.references ?? []).map((path) => `reference to consult (read-only): ${path}`),
28
+ ]
29
+ : [
30
+ `object artifacts to read, in order: ${request.objects.join(', ')}`,
31
+ `link target module: ${request.linkTarget}`,
32
+ `options: ${formatOptions(request.options)}`,
33
+ ];
34
+ return [
35
+ 'You are executing one phase of the SubLang Compiler (slc).',
36
+ 'The phase definition below is authoritative; follow it exactly and add no rules of your own.',
37
+ '',
38
+ '--- DEFINITION ---',
39
+ definition,
40
+ '--- END DEFINITION ---',
41
+ '',
42
+ 'Inputs:',
43
+ ...inputs.map((line) => `- ${line}`),
44
+ `- artifact to write: ${target}`,
45
+ '',
46
+ 'Contract — you must:',
47
+ `- write only ${target}, creating or overwriting exactly that file;`,
48
+ '- not edit the sources, the phase or link definition, specs, link targets, object artifacts, or any other file;',
49
+ '- not commit or otherwise touch version control;',
50
+ '- produce a complete artifact, not a sketch or placeholder;',
51
+ '- add no domain semantics beyond what the source implies or the definition requires, and drop nothing the source states;',
52
+ '- preserve verbatim any content the definition requires to be preserved;',
53
+ '- run only the deterministic tools or commands the definition calls for, and read only the content it cites or references;',
54
+ '- verify the produced artifact against the definition before finishing.',
55
+ '',
56
+ 'When done, reply with a concise summary of what you produced and any ambiguity you resolved.',
57
+ 'If the inputs are malformed under the definition, or the definition is incompatible with them, do not guess: leave the artifact unwritten and reply with a line beginning "BLOCKED:" followed by the concrete reason(s).',
58
+ ].join('\n');
59
+ }
60
+ /**
61
+ * Creates a {@link PhaseExecutor} that interprets a phase via one agent
62
+ * invocation (DR-004). Plugs into the DR-003 boundary (`runPhase`), which
63
+ * applies the generic checks to whatever the agent wrote.
64
+ */
65
+ export function createInterpretedExecutor(opts) {
66
+ const { agent } = opts;
67
+ const config = opts.config ?? {};
68
+ return {
69
+ async run(request, signal) {
70
+ const definition = await readFile(request.definitionPath, 'utf8');
71
+ const prompt = buildPhasePrompt({ request, definition });
72
+ const response = await agent.run({
73
+ prompt,
74
+ cwd: config.cwd,
75
+ model: config.model,
76
+ signal,
77
+ });
78
+ if (response.status !== 'success') {
79
+ const fallback = response.status === 'incomplete'
80
+ ? 'agent did not finish'
81
+ : 'agent reported an error';
82
+ return {
83
+ status: 'error',
84
+ diagnostics: textLines(response.text, fallback),
85
+ };
86
+ }
87
+ const blocked = blockedReasons(response.text);
88
+ if (blocked !== null) {
89
+ return { status: 'blocked', diagnostics: blocked };
90
+ }
91
+ return { status: 'ok', diagnostics: textLines(response.text) };
92
+ },
93
+ };
94
+ }
95
+ function formatOptions(options) {
96
+ if (options.length === 0)
97
+ return '(none)';
98
+ return options.map((option) => `${option.name}=${option.value}`).join(', ');
99
+ }
100
+ /** Returns the `BLOCKED:` diagnostic lines, or `null` when the agent did not block. */
101
+ function blockedReasons(text) {
102
+ const lines = text.split(/\r?\n/).map((line) => line.trim());
103
+ const start = lines.findIndex((line) => /^BLOCKED\b/i.test(line));
104
+ if (start === -1)
105
+ return null;
106
+ const reasons = lines.slice(start).filter((line) => line.length > 0);
107
+ return reasons.length > 0 ? reasons : ['BLOCKED'];
108
+ }
109
+ function textLines(text, fallback) {
110
+ const lines = text
111
+ .split(/\r?\n/)
112
+ .map((line) => line.trim())
113
+ .filter((line) => line.length > 0);
114
+ if (lines.length > 0)
115
+ return lines;
116
+ return fallback ? [fallback] : [];
117
+ }
118
+ //# sourceMappingURL=interpreter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interpreter.js","sourceRoot":"","sources":["../src/interpreter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * CLI parsing and invocation routing (DR-001, DR-002).
3
+ *
4
+ * Implements PIPE-9 (`slc <pipeline>[.<phase>] <source> [-o <target>]`), PIPE-12
5
+ * (`slc <pipeline>.link <object>... <target>` with positional, not extension- or
6
+ * `--`-inferred, roles), PIPE-13 (`--link` selects the terminal link phase only
7
+ * for full-pipeline runs), and PIPE-14 (opaque `--link-option name=value` pairs).
8
+ * See specs/dev/pipeline.md.
9
+ */
10
+ /** An opaque link option passed through to the link phase (PIPE-14). */
11
+ export interface LinkOption {
12
+ name: string;
13
+ value: string;
14
+ }
15
+ /** A parsed `slc` invocation, routed to one of the four run forms. */
16
+ export type Invocation = {
17
+ kind: 'full';
18
+ pipeline: string;
19
+ source: string;
20
+ output: string | null;
21
+ optimize: boolean;
22
+ noOptimize: boolean;
23
+ normalize: boolean;
24
+ } | {
25
+ kind: 'phase';
26
+ pipeline: string;
27
+ phase: string;
28
+ source: string;
29
+ output: string | null;
30
+ } | {
31
+ kind: 'full-link';
32
+ pipeline: string;
33
+ source: string;
34
+ linkTarget: string;
35
+ output: string | null;
36
+ options: LinkOption[];
37
+ optimize: boolean;
38
+ noOptimize: boolean;
39
+ normalize: boolean;
40
+ } | {
41
+ kind: 'link';
42
+ pipeline: string;
43
+ objects: string[];
44
+ linkTarget: string;
45
+ output: string | null;
46
+ options: LinkOption[];
47
+ };
48
+ /** Machine-readable reason an invocation was refused. */
49
+ export type CliErrorCode = 'no-pipeline' | 'operands' | 'unexpected-link' | 'unexpected-link-option' | 'unexpected-flag' | 'option-value' | 'invalid-link-option' | 'unknown-option' | 'duplicate-option';
50
+ /** Raised when argv does not parse to a valid invocation (DR-001, DR-002). */
51
+ export declare class CliError extends Error {
52
+ readonly code: CliErrorCode;
53
+ constructor(code: CliErrorCode, message: string);
54
+ }
55
+ /**
56
+ * Parses argv (without the node/script prefix) into a routed {@link Invocation}.
57
+ *
58
+ * Options (`-o`, `--link`, `--link-option`) may be interspersed with positionals;
59
+ * positional roles are assigned by position alone (PIPE-12).
60
+ *
61
+ * @throws {CliError} when the grammar is violated.
62
+ */
63
+ export declare function parseInvocation(argv: readonly string[]): Invocation;
64
+ //# sourceMappingURL=invocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation.d.ts","sourceRoot":"","sources":["../src/invocation.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,wEAAwE;AACxE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAClB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,GACD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB,CAAC;AAEN,yDAAyD;AACzD,MAAM,MAAM,YAAY,GACpB,aAAa,GACb,UAAU,GACV,iBAAiB,GACjB,wBAAwB,GACxB,iBAAiB,GACjB,cAAc,GACd,qBAAqB,GACrB,gBAAgB,GAChB,kBAAkB,CAAC;AAEvB,8EAA8E;AAC9E,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;gBAEhB,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM;CAKhD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAkInE"}
@@ -0,0 +1,148 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // SPDX-FileCopyrightText: 2026 SubLang International <https://sublang.ai>
3
+ /** Raised when argv does not parse to a valid invocation (DR-001, DR-002). */
4
+ export class CliError extends Error {
5
+ code;
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.name = 'CliError';
9
+ this.code = code;
10
+ }
11
+ }
12
+ /**
13
+ * Parses argv (without the node/script prefix) into a routed {@link Invocation}.
14
+ *
15
+ * Options (`-o`, `--link`, `--link-option`) may be interspersed with positionals;
16
+ * positional roles are assigned by position alone (PIPE-12).
17
+ *
18
+ * @throws {CliError} when the grammar is violated.
19
+ */
20
+ export function parseInvocation(argv) {
21
+ const positionals = [];
22
+ let output = null;
23
+ let link = null;
24
+ let optimize = false;
25
+ let noOptimize = false;
26
+ let normalize = false;
27
+ const options = [];
28
+ for (let i = 0; i < argv.length; i++) {
29
+ const arg = argv[i];
30
+ if (arg === '-o') {
31
+ if (output !== null)
32
+ throw new CliError('duplicate-option', 'repeated -o');
33
+ output = takeValue(argv, ++i, '-o');
34
+ }
35
+ else if (arg === '--link') {
36
+ if (link !== null)
37
+ throw new CliError('duplicate-option', 'repeated --link');
38
+ link = takeValue(argv, ++i, '--link');
39
+ }
40
+ else if (arg === '--link-option') {
41
+ options.push(parseLinkOption(takeValue(argv, ++i, '--link-option')));
42
+ }
43
+ else if (arg === '-O' || arg === '--optimize') {
44
+ optimize = true;
45
+ }
46
+ else if (arg === '--no-optimize') {
47
+ noOptimize = true;
48
+ }
49
+ else if (arg === '--normalize') {
50
+ normalize = true;
51
+ }
52
+ else if (arg.startsWith('-') && arg !== '-') {
53
+ throw new CliError('unknown-option', `unknown option "${arg}"`);
54
+ }
55
+ else {
56
+ positionals.push(arg);
57
+ }
58
+ }
59
+ if (positionals.length === 0) {
60
+ throw new CliError('no-pipeline', 'missing <pipeline> operand');
61
+ }
62
+ const head = positionals[0];
63
+ const dot = head.indexOf('.');
64
+ const pipeline = dot === -1 ? head : head.slice(0, dot);
65
+ const phase = dot === -1 ? null : head.slice(dot + 1);
66
+ if (pipeline.length === 0 || phase === '') {
67
+ throw new CliError('no-pipeline', `invalid pipeline reference "${head}"`);
68
+ }
69
+ const rest = positionals.slice(1);
70
+ if (optimize && noOptimize) {
71
+ throw new CliError('duplicate-option', '-O/--optimize conflicts with --no-optimize');
72
+ }
73
+ if (phase !== null && (optimize || noOptimize || normalize)) {
74
+ throw new CliError('unexpected-flag', `${optimize ? '-O' : noOptimize ? '--no-optimize' : '--normalize'} is only valid for a full-pipeline invocation`);
75
+ }
76
+ if (phase === 'link') {
77
+ if (link !== null) {
78
+ throw new CliError('unexpected-link', '--link is not valid with a .link invocation');
79
+ }
80
+ if (rest.length < 2) {
81
+ throw new CliError('operands', '.link requires at least one object and a target');
82
+ }
83
+ return {
84
+ kind: 'link',
85
+ pipeline,
86
+ objects: rest.slice(0, -1),
87
+ linkTarget: rest[rest.length - 1],
88
+ output,
89
+ options,
90
+ };
91
+ }
92
+ if (phase !== null) {
93
+ if (link !== null) {
94
+ throw new CliError('unexpected-link', '--link is only valid for a full-pipeline invocation');
95
+ }
96
+ if (options.length > 0) {
97
+ throw new CliError('unexpected-link-option', '--link-option requires a link phase');
98
+ }
99
+ requireSingleSource(rest);
100
+ return { kind: 'phase', pipeline, phase, source: rest[0], output };
101
+ }
102
+ requireSingleSource(rest);
103
+ const source = rest[0];
104
+ if (link !== null) {
105
+ return {
106
+ kind: 'full-link',
107
+ pipeline,
108
+ source,
109
+ linkTarget: link,
110
+ output,
111
+ options,
112
+ optimize,
113
+ noOptimize,
114
+ normalize,
115
+ };
116
+ }
117
+ if (options.length > 0) {
118
+ throw new CliError('unexpected-link-option', '--link-option requires --link');
119
+ }
120
+ return {
121
+ kind: 'full',
122
+ pipeline,
123
+ source,
124
+ output,
125
+ optimize,
126
+ noOptimize,
127
+ normalize,
128
+ };
129
+ }
130
+ function takeValue(argv, index, flag) {
131
+ if (index >= argv.length) {
132
+ throw new CliError('option-value', `missing value for ${flag}`);
133
+ }
134
+ return argv[index];
135
+ }
136
+ function parseLinkOption(token) {
137
+ const eq = token.indexOf('=');
138
+ if (eq <= 0) {
139
+ throw new CliError('invalid-link-option', `--link-option must be name=value, got "${token}"`);
140
+ }
141
+ return { name: token.slice(0, eq), value: token.slice(eq + 1) };
142
+ }
143
+ function requireSingleSource(rest) {
144
+ if (rest.length !== 1) {
145
+ throw new CliError('operands', `expected exactly one <source>, got ${rest.length}`);
146
+ }
147
+ }
148
+ //# sourceMappingURL=invocation.js.map