@owlmeans/llm-common 0.1.18-rc.13 → 0.1.18-rc.15

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/README.md CHANGED
@@ -10,8 +10,6 @@ persistence/queue consumer need to name the same things.
10
10
  - The inheritable `ModelPolicy` and its JSON-safe `ModelConfigPatch` / `ModelConfigOverride`
11
11
  - `ExecutionState` / `TaskExecutionState` — what an execution looks like once its
12
12
  collaborators (models, file access, live handles) are stripped off
13
- - The inquiry contracts — one question put to a person mid-run, the answer that comes back,
14
- and the pure helpers every layer reads an answer through
15
13
  - Spectator record contracts and the `NullCapture` diagnostic
16
14
  - No `@langchain/*` runtime dependency: safe to import from a browser bundle, a queue
17
15
  worker, or a package that must not pull an inference SDK
@@ -65,13 +63,12 @@ export interface MyExecutionState extends ExecutionState {
65
63
 
66
64
  | Export | Description |
67
65
  |--------|-------------|
68
- | `ModelProvider` | `OpenAI` · `Anthropic` · `Compatible` · `Delegated` — each maps to an `LlmPlugin` type in `@owlmeans/llm`. `Delegated` talks to no provider: the call is handed to a transport the application seated (`@owlmeans/llm-delegate`). |
66
+ | `ModelProvider` | `OpenAI` · `Anthropic` · `Compatible` — each maps to an `LlmPlugin` type in `@owlmeans/llm`. |
69
67
  | `ExecutionLevel` | `Project` → `Task` → `Helper`; an execution is refined downward only. |
70
68
  | `ExecutionEffort` | `Economy` · `Standard` · `High` · `Max` — the one "how hard should this run" axis. |
71
69
  | `StructuredMode` | `Native` (provider JSON-schema mode) vs `Tool` (forced tool call). |
72
70
  | `SpectatorContentType` | `Text` · `Json` · `ToolCall`. |
73
71
  | `SPECTATOR_GENERAL` | Default entry kind for consumers that do not classify calls. |
74
- | `DEFAULT_INQUIRY_ANSWER_CHARS` (2000) · `DEFAULT_INQUIRY_OPTIONS` (12) · `INQUIRY_STATE_TEXT_CHARS` (200) · `CONFIRM_YES` / `CONFIRM_NO` | The one ceiling on a stored answer, the point past which a choice is not a question, what a pipeline state keeps of an answer's prose, and the two confirmation values. Never introduce a second copy of any of them. |
75
72
 
76
73
  ### Types
77
74
 
@@ -85,9 +82,6 @@ export interface MyExecutionState extends ExecutionState {
85
82
  | `TaskExecutionState` | Adds `phase` / `completed` / `cursor` / `data` for checkpoint & resume. |
86
83
  | `LlmPurpose` | `{ type?, dedication? }` — observability metadata carried on every call. |
87
84
  | `NullCapture`, `NullKind` | Full diagnostics of a call that returned nothing usable. |
88
- | `Inquiry`, `InquiryAnswer`, `InquiryOption`, `InquiryTransport`, `InquiryConfig` | One question put to a person while a run is in flight, and its answer. `ExecutionState.inquiry` carries the channel and the policy, so a resumed run asks the same way. |
89
- | `InquiryKind`, `InquiryPolicy` | `choice` / `text` / `confirm`; `ask` / `default` / `refuse` — no configuration means `default`, so a run nobody is watching never blocks on a question. |
90
- | `defaultAnswerFor`, `answeredWith`, `isDeclined`, `capAnswer`, `stateAnswerOf`, `renderInquiry` | The ONE reading of an answer every layer shares. `capAnswer` cuts prose only and reports the cut; a `value` is the decision itself and is never shortened. |
91
85
  | `SpectatorArgument`, `SpectatorEntry`, `SpectatorEntryLogged`, `SpectatorEntryMessage` | The record format an observability sink stores. |
92
86
 
93
87
  ## Related
@@ -102,7 +96,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
102
96
  your project's skill store (`.agents/skills/`):
103
97
 
104
98
  ```sh
105
- npx @owlmeans/agent-skills@^0.1.18-rc.13
99
+ npx @owlmeans/agent-skills@^0.1.18-rc.15
106
100
  ```
107
101
 
108
102
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/llm-common",
4
- "version": "0.1.18-rc.13",
5
- "generatedAt": "2026-09-10T18:54:42.500Z",
4
+ "version": "0.1.18-rc.15",
5
+ "generatedAt": "2026-09-12T08:39:52.730Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/llm-common
9
9
 
10
10
  **Layer:** Core
11
- **Install:** `"@owlmeans/llm-common": "^0.1.18-rc.13"` in `dependencies`
11
+ **Install:** `"@owlmeans/llm-common": "^0.1.18-rc.15"` in `dependencies`
12
12
 
13
13
  The contracts half of the LLM stack. **No `@langchain/*` runtime dependency** — importable
14
14
  from a browser bundle, a queue worker, or any package that must not pull an inference SDK.
@@ -36,10 +36,6 @@ The dependency direction is one-way: a domain contracts package extends these;
36
36
  | `CacheTtl`, `CacheUsage` | `'5m' \| '1h'`; normalized prompt-cache accounting. |
37
37
  | `LlmFileProvider`, `FileProviderRef`, `resolveFileProvider` | The file contract prompt plugins work against — four members, every path relative to the host's project root. `FileProviderRef` accepts the provider or a thunk returning one; `resolveFileProvider` unwraps whichever form arrived, or `undefined`. |
38
38
  | `NullCapture`, `NullKind` | Full diagnostics of a call that returned nothing usable. |
39
- | `Inquiry`, `InquiryAnswer`, `InquiryOption`, `InquiryTransport`, `InquiryConfig` | One question put to a person mid-run, and the answer that comes back. |
40
- | `InquiryKind`, `InquiryPolicy` | `choice`/`text`/`confirm`; `ask`/`default`/`refuse`. |
41
- | `DEFAULT_INQUIRY_ANSWER_CHARS`, `DEFAULT_INQUIRY_OPTIONS`, `INQUIRY_STATE_TEXT_CHARS`, `CONFIRM_YES`, `CONFIRM_NO` | The one answer ceiling, the option ceiling, what a pipeline state may hold of an answer's prose, and the two confirmation values. |
42
- | `defaultAnswerFor`, `answeredWith`, `capAnswer`, `isDeclined`, `stateAnswerOf`, `renderInquiry` | The pure reading of an inquiry and its answer, shared by every layer. |
43
39
  | `SpectatorArgument`, `SpectatorEntry`, `SpectatorEntryLogged`, `SpectatorEntryMessage` | What an observability sink stores. |
44
40
 
45
41
  ## `LlmFileProvider` — what a host must supply
@@ -90,35 +86,12 @@ export interface MyTaskState
90
86
  `SpectatorEntry.kind` is an open `string` for the same reason: declare your own kind enum
91
87
  and narrow it on your own entry interface.
92
88
 
93
- ## Inquiry — the contracts half
94
-
95
- `src/inquiry/` carries the human-in-the-loop primitive's serializable half: the question, the
96
- answer, the transport interface, and the pure helpers every layer reads an answer through.
97
- `ExecutionState.inquiry?: InquiryConfig` says how one run may put a question to a person, and it is
98
- STATE rather than a collaborator — a resumed run must ask through the same channel under the same
99
- policy, so it travels into every snapshot (it is deliberately absent from `@owlmeans/llm`'s
100
- `COLLABORATOR_KEYS`).
101
-
102
- `DEFAULT_INQUIRY_ANSWER_CHARS` is the ONE ceiling on a stored answer; every other layer references
103
- it rather than choosing its own, and `capAnswer` reports the cut (`truncated: true`) instead of
104
- truncating silently. It cuts the PROSE only: a `value` is the decision itself, so a shortened one
105
- matches no option and is passed through whole with `truncated` raised on it.
106
- `INQUIRY_STATE_TEXT_CHARS` is the much smaller amount a resumable pipeline
107
- state may hold of an answer's free text (`stateAnswerOf`, the other writer of `truncated`) — a
108
- state is keys and markers, not prose.
109
- The primitive end to end, including the runtime registry and the pipeline park: [[inquiry]].
110
-
111
89
  ## What must NOT go here
112
90
 
113
91
  Anything that cannot survive `JSON.stringify` or that needs an inference SDK: model
114
92
  instances, credentials, file handles, callbacks, `ModelConfig` (it carries `secret` /
115
93
  `headers` / `fallback` — that lives in `@owlmeans/llm`).
116
94
 
117
- ## Tests
118
-
119
- `bun test ./tests` in the package. Contracts and enums are not tested (nothing to assert); the
120
- specs cover the pure helpers that carry a decision — `tests/inquiry.spec.ts`.
121
-
122
95
  ## Depends On
123
96
 
124
97
  Nothing at runtime. `@langchain/core` is a **dev** dependency, for the `UsageMetadata` type
@@ -127,4 +100,3 @@ on a spectator message only.
127
100
  ## Related
128
101
 
129
102
  - [[llm]] — the runtime that implements these contracts
130
- - [[inquiry]] — the human-in-the-loop primitive whose contracts live here
package/build/index.d.ts CHANGED
@@ -4,5 +4,4 @@ export type * from './spectator/types.js';
4
4
  export type * from './files/types.js';
5
5
  export * from './files/utils.js';
6
6
  export * from './delegate/index.js';
7
- export * from './inquiry/index.js';
8
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,mBAAmB,YAAY,CAAA;AAC/B,mBAAmB,sBAAsB,CAAA;AACzC,mBAAmB,kBAAkB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAC3B,mBAAmB,YAAY,CAAA;AAC/B,mBAAmB,sBAAsB,CAAA;AACzC,mBAAmB,kBAAkB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA"}
package/build/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from './consts.js';
2
2
  export * from './files/utils.js';
3
3
  export * from './delegate/index.js';
4
- export * from './inquiry/index.js';
5
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAI3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA;AAI3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA"}
package/build/types.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { ExecutionEffort, ExecutionLevel, PromptBlock } from './consts.js';
2
- import type { InquiryConfig } from './inquiry/types.js';
3
2
  /**
4
3
  * Free-form observability metadata attached to every model call — forwarded to the
5
4
  * inference provider as run metadata and recorded on every spectator entry. Kept
@@ -132,12 +131,6 @@ export interface ExecutionState {
132
131
  policy: ModelPolicy;
133
132
  /** Role + skills for this level; merged downward by `ExecutionService`. */
134
133
  prompt?: PromptPolicy;
135
- /**
136
- * How this run may put a question to a person. Serializable, and deliberately NOT a
137
- * collaborator: a run that is resumed days later must ask through the same channel, under the
138
- * same policy, as the one that parked it.
139
- */
140
- inquiry?: InquiryConfig;
141
134
  }
142
135
  /**
143
136
  * The task level's own fields.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "@owlmeans/llm-common",
3
- "version": "0.1.18-rc.13",
3
+ "version": "0.1.18-rc.15",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "tsc -b",
8
8
  "dev": "sleep 171 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
9
- "watch": "tsc -b -w --preserveWatchOutput --pretty",
10
- "test": "bun test ./tests"
9
+ "watch": "tsc -b -w --preserveWatchOutput --pretty"
11
10
  },
12
11
  "main": "build/index.js",
13
12
  "module": "build/index.js",
@@ -24,7 +23,6 @@
24
23
  "devDependencies": {
25
24
  "@langchain/core": "^1.2.9",
26
25
  "@owlmeans/dep-config": "workspace:*",
27
- "@types/bun": "^1.4.0",
28
26
  "nodemon": "^3.1.14",
29
27
  "typescript": "^7.0.2"
30
28
  },
package/src/index.ts CHANGED
@@ -5,4 +5,3 @@ export type * from './spectator/types.js'
5
5
  export type * from './files/types.js'
6
6
  export * from './files/utils.js'
7
7
  export * from './delegate/index.js'
8
- export * from './inquiry/index.js'
package/src/types.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { ExecutionEffort, ExecutionLevel, PromptBlock } from './consts.js'
2
- import type { InquiryConfig } from './inquiry/types.js'
3
2
 
4
3
  /**
5
4
  * Free-form observability metadata attached to every model call — forwarded to the
@@ -142,12 +141,6 @@ export interface ExecutionState {
142
141
  policy: ModelPolicy
143
142
  /** Role + skills for this level; merged downward by `ExecutionService`. */
144
143
  prompt?: PromptPolicy
145
- /**
146
- * How this run may put a question to a person. Serializable, and deliberately NOT a
147
- * collaborator: a run that is resumed days later must ask through the same channel, under the
148
- * same policy, as the one that parked it.
149
- */
150
- inquiry?: InquiryConfig
151
144
  }
152
145
 
153
146
  /**
package/tsconfig.json CHANGED
@@ -10,8 +10,6 @@
10
10
  ],
11
11
  "exclude": [
12
12
  "./dist/**/*",
13
- "./build/**/*",
14
- "./tests/**/*",
15
- "./*.ts"
13
+ "./build/**/*"
16
14
  ]
17
15
  }
@@ -1,48 +0,0 @@
1
- /**
2
- * What shape of answer a question expects. Deliberately three, because a question a person is
3
- * asked mid-run is answered in seconds or not at all: pick one of these, say it in your own
4
- * words, or say yes/no. Anything richer is a form, and a form belongs to an application screen.
5
- */
6
- export declare enum InquiryKind {
7
- Choice = "choice",
8
- Text = "text",
9
- Confirm = "confirm"
10
- }
11
- /**
12
- * What a run is allowed to do when it needs a decision that is not its own.
13
- *
14
- * - `Ask` — put it to a person through the seated transport and wait.
15
- * - `Default` — assume the question's own default (or record a decline) and carry on. The caller
16
- * is expected to RECORD the assumption; a run nobody is watching must never block.
17
- * - `Refuse` — nobody may be asked at all; the attempt is an error.
18
- */
19
- export declare enum InquiryPolicy {
20
- Ask = "ask",
21
- Default = "default",
22
- Refuse = "refuse"
23
- }
24
- /**
25
- * The ONE ceiling on a stored answer, in characters. An answer is a decision, not a document.
26
- *
27
- * Every layer that carries an answer references this constant rather than choosing its own:
28
- * `viable-common`'s `CONNECT_INQUIRY_MAX_TEXT` (the wire copy) equals it, `capAnswer` enforces it,
29
- * and the connector's answer schema caps `text` at it. Three ceilings for one value is how a
30
- * user's answer gets accepted on the wire and silently halved further in.
31
- */
32
- export declare const DEFAULT_INQUIRY_ANSWER_CHARS = 2000;
33
- /** How many options one question may offer. Beyond this it is not a question. */
34
- export declare const DEFAULT_INQUIRY_OPTIONS = 12;
35
- /**
36
- * How much of an answer's free text a resumable pipeline STATE may hold.
37
- *
38
- * A pipeline state is keys, markers and paths — a couple of full-size answers would make it prose,
39
- * which is the invariant the whole pipeline design rests on. The decision (`value`/`declined`)
40
- * stays whole; the prose is cut here and belongs in whatever document the application keeps for
41
- * it (`stateAnswerOf`).
42
- */
43
- export declare const INQUIRY_STATE_TEXT_CHARS = 200;
44
- /** The answer value of a confirmed {@link InquiryKind.Confirm}. */
45
- export declare const CONFIRM_YES = "yes";
46
- /** The answer value of a refused {@link InquiryKind.Confirm}. */
47
- export declare const CONFIRM_NO = "no";
48
- //# sourceMappingURL=consts.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../../src/inquiry/consts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,oBAAY,WAAW;IACrB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,OAAO,YAAY;CACpB;AAED;;;;;;;GAOG;AACH,oBAAY,aAAa;IACvB,GAAG,QAAQ;IACX,OAAO,YAAY;IACnB,MAAM,WAAW;CAClB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,OAAQ,CAAA;AAEjD,iFAAiF;AACjF,eAAO,MAAM,uBAAuB,KAAK,CAAA;AAEzC;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAE3C,mEAAmE;AACnE,eAAO,MAAM,WAAW,QAAQ,CAAA;AAChC,iEAAiE;AACjE,eAAO,MAAM,UAAU,OAAO,CAAA"}
@@ -1,50 +0,0 @@
1
- /**
2
- * What shape of answer a question expects. Deliberately three, because a question a person is
3
- * asked mid-run is answered in seconds or not at all: pick one of these, say it in your own
4
- * words, or say yes/no. Anything richer is a form, and a form belongs to an application screen.
5
- */
6
- export var InquiryKind;
7
- (function (InquiryKind) {
8
- InquiryKind["Choice"] = "choice";
9
- InquiryKind["Text"] = "text";
10
- InquiryKind["Confirm"] = "confirm";
11
- })(InquiryKind || (InquiryKind = {}));
12
- /**
13
- * What a run is allowed to do when it needs a decision that is not its own.
14
- *
15
- * - `Ask` — put it to a person through the seated transport and wait.
16
- * - `Default` — assume the question's own default (or record a decline) and carry on. The caller
17
- * is expected to RECORD the assumption; a run nobody is watching must never block.
18
- * - `Refuse` — nobody may be asked at all; the attempt is an error.
19
- */
20
- export var InquiryPolicy;
21
- (function (InquiryPolicy) {
22
- InquiryPolicy["Ask"] = "ask";
23
- InquiryPolicy["Default"] = "default";
24
- InquiryPolicy["Refuse"] = "refuse";
25
- })(InquiryPolicy || (InquiryPolicy = {}));
26
- /**
27
- * The ONE ceiling on a stored answer, in characters. An answer is a decision, not a document.
28
- *
29
- * Every layer that carries an answer references this constant rather than choosing its own:
30
- * `viable-common`'s `CONNECT_INQUIRY_MAX_TEXT` (the wire copy) equals it, `capAnswer` enforces it,
31
- * and the connector's answer schema caps `text` at it. Three ceilings for one value is how a
32
- * user's answer gets accepted on the wire and silently halved further in.
33
- */
34
- export const DEFAULT_INQUIRY_ANSWER_CHARS = 2_000;
35
- /** How many options one question may offer. Beyond this it is not a question. */
36
- export const DEFAULT_INQUIRY_OPTIONS = 12;
37
- /**
38
- * How much of an answer's free text a resumable pipeline STATE may hold.
39
- *
40
- * A pipeline state is keys, markers and paths — a couple of full-size answers would make it prose,
41
- * which is the invariant the whole pipeline design rests on. The decision (`value`/`declined`)
42
- * stays whole; the prose is cut here and belongs in whatever document the application keeps for
43
- * it (`stateAnswerOf`).
44
- */
45
- export const INQUIRY_STATE_TEXT_CHARS = 200;
46
- /** The answer value of a confirmed {@link InquiryKind.Confirm}. */
47
- export const CONFIRM_YES = 'yes';
48
- /** The answer value of a refused {@link InquiryKind.Confirm}. */
49
- export const CONFIRM_NO = 'no';
50
- //# sourceMappingURL=consts.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"consts.js","sourceRoot":"","sources":["../../src/inquiry/consts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAN,IAAY,WAIX;AAJD,WAAY,WAAW;IACrB,gCAAiB,CAAA;IACjB,4BAAa,CAAA;IACb,kCAAmB,CAAA;AACrB,CAAC,EAJW,WAAW,KAAX,WAAW,QAItB;AAED;;;;;;;GAOG;AACH,MAAM,CAAN,IAAY,aAIX;AAJD,WAAY,aAAa;IACvB,4BAAW,CAAA;IACX,oCAAmB,CAAA;IACnB,kCAAiB,CAAA;AACnB,CAAC,EAJW,aAAa,KAAb,aAAa,QAIxB;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAA;AAEjD,iFAAiF;AACjF,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAA;AAEzC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAA;AAE3C,mEAAmE;AACnE,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,CAAA;AAChC,iEAAiE;AACjE,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAA"}
@@ -1,4 +0,0 @@
1
- export * from './consts.js';
2
- export type * from './types.js';
3
- export * from './utils.js';
4
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/inquiry/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA"}
@@ -1,3 +0,0 @@
1
- export * from './consts.js';
2
- export * from './utils.js';
3
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/inquiry/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAE3B,cAAc,YAAY,CAAA"}
@@ -1,72 +0,0 @@
1
- import type { InquiryKind, InquiryPolicy } from './consts.js';
2
- /**
3
- * One question put to a person while a run is in flight, and the answer that comes back.
4
- *
5
- * Everything here is serializable for the same reason a delegated task is: whoever answers is
6
- * outside this process — a browser dialog, a coding agent driving the application through a
7
- * connector, a test double — and the question may outlive the process that asked it, parked on a
8
- * pipeline run row until somebody comes back to it.
9
- */
10
- export interface InquiryOption {
11
- value: string;
12
- label: string;
13
- description?: string;
14
- }
15
- export interface Inquiry {
16
- /** Stable id, chosen by whoever asks. The ONLY thing that routes an answer back. */
17
- id: string;
18
- kind: InquiryKind;
19
- /** One question, in plain words. */
20
- question: string;
21
- /** One or two sentences of background. Never the whole task. */
22
- context?: string;
23
- /** Required for {@link InquiryKind.Choice}; ignored otherwise. */
24
- options?: InquiryOption[];
25
- multiple?: boolean;
26
- /** A `Choice` the answerer may answer in their own words instead. */
27
- allowText?: boolean;
28
- /** What to assume when nobody answers. {@link InquiryPolicy.Default} returns exactly this. */
29
- default?: string | string[];
30
- expiresAt?: string;
31
- }
32
- export interface InquiryAnswer {
33
- inquiryId: string;
34
- value?: string | string[];
35
- text?: string;
36
- /** Nobody could decide. A legitimate ANSWER, never a failure. */
37
- declined?: boolean;
38
- /**
39
- * This answer is not exactly the one that was given. Never an answerer's own flag.
40
- *
41
- * Two writers, two ceilings: `capAnswer` cuts the text to `DEFAULT_INQUIRY_ANSWER_CHARS` (and
42
- * raises this WITHOUT cutting when a `value` arrived over that ceiling, since a shortened
43
- * identifier matches no option), while `stateAnswerOf` cuts the text again to the much smaller
44
- * `INQUIRY_STATE_TEXT_CHARS` a pipeline state may hold. So a flag read back off a resumed run
45
- * says the state's copy is short — not that the person hit the answer ceiling.
46
- *
47
- * It exists so no cut is silent: whoever records the answer can say that the rest of it was
48
- * dropped, instead of the answerer discovering it in the work that followed.
49
- */
50
- truncated?: boolean;
51
- }
52
- /**
53
- * How a question reaches a person.
54
- *
55
- * One method, like `DelegateTransport` beside it, and for the same reason: routing, waiting,
56
- * redelivery and giving up all belong to whoever implements it. A transport that cannot serve the
57
- * question must THROW rather than answer — a declined answer is a decision, while a channel that
58
- * is not there is terminal, and the two must never look alike.
59
- */
60
- export interface InquiryTransport {
61
- ask: (inquiry: Inquiry, signal?: AbortSignal) => Promise<InquiryAnswer>;
62
- }
63
- /**
64
- * How one run may put a question to a person. Carried on an execution's serializable state, so a
65
- * resumed run keeps the channel and the policy it was started with.
66
- */
67
- export interface InquiryConfig {
68
- /** The key the application seated an {@link InquiryTransport} under. */
69
- transport?: string;
70
- policy: InquiryPolicy;
71
- }
72
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/inquiry/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE7D;;;;;;;GAOG;AAEH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,WAAW,CAAA;IACjB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kEAAkE;IAClE,OAAO,CAAC,EAAE,aAAa,EAAE,CAAA;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;CACxE;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,aAAa,CAAA;CACtB"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/inquiry/types.ts"],"names":[],"mappings":""}
@@ -1,45 +0,0 @@
1
- import type { Inquiry, InquiryAnswer } from './types.js';
2
- /**
3
- * Pure helpers over an inquiry and its answer — no IO, no state, no clock.
4
- *
5
- * They are here rather than in the runtime because every layer needs the same reading of an
6
- * answer: the execution service, the pipeline runner, the `ask_user` tool, the connector and the
7
- * screen that finally shows it. A second reading of "was this answered" is a second contract.
8
- */
9
- /**
10
- * What to assume when nobody answers: the question's own default, or a decline.
11
- *
12
- * A decline is an ANSWER — the run carries on and records what it assumed — which is why this
13
- * never throws and never returns null.
14
- */
15
- export declare const defaultAnswerFor: (inquiry: Inquiry) => InquiryAnswer;
16
- /**
17
- * The one thing an answer says, as a string — the first chosen value, else the free text, else
18
- * `null` when it says nothing at all (a decline, or an empty answer).
19
- */
20
- export declare const answeredWith: (answer: InquiryAnswer) => string | null;
21
- /** Nobody could decide. Distinct from an empty answer — see {@link answeredWith}. */
22
- export declare const isDeclined: (answer: InquiryAnswer) => boolean;
23
- /**
24
- * Cut an answer's free TEXT to the ceiling and SAY SO.
25
- *
26
- * Only the prose is cut. A `value` IS the decision — for a choice it has to equal one of the
27
- * question's own option values — so slicing one does not degrade an answer, it silently replaces
28
- * it with an identifier nobody offered and nothing matches, which `answeredWith` then hands on as
29
- * what the person chose. An over-long value is a defect upstream rather than a long answer (the
30
- * connector's schema refuses one outright instead of shortening it), so it travels on whole and is
31
- * REPORTED through `truncated` — the flag every consumer already reads as "this is not exactly
32
- * what the person gave".
33
- */
34
- export declare const capAnswer: (answer: InquiryAnswer, max?: number) => InquiryAnswer;
35
- /**
36
- * The copy of an answer a resumable pipeline STATE keeps: the decision whole, the prose cut to
37
- * {@link INQUIRY_STATE_TEXT_CHARS}.
38
- *
39
- * The full answer goes back to whoever asked; only this reduced one is persisted, so a run that
40
- * asks several questions still holds a state made of keys rather than of paragraphs.
41
- */
42
- export declare const stateAnswerOf: (answer: InquiryAnswer) => InquiryAnswer;
43
- /** One-line label of a question — for a note, a trace line or a run row. */
44
- export declare const renderInquiry: (inquiry: Inquiry) => string;
45
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/inquiry/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAExD;;;;;;GAMG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,YAAa,OAAO,KAAG,aAGL,CAAA;AAE/C;;;GAGG;AACH,eAAO,MAAM,YAAY,WAAY,aAAa,KAAG,MAAM,GAAG,IAM7D,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,UAAU,WAAY,aAAa,KAAG,OAAmC,CAAA;AAEtF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,WACZ,aAAa,QAAO,MAAM,KACjC,aAYF,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,WAAY,aAAa,KAAG,aAG1C,CAAA;AAEZ,4EAA4E;AAC5E,eAAO,MAAM,aAAa,YAAa,OAAO,KAAG,MAMhD,CAAA"}
@@ -1,72 +0,0 @@
1
- import { DEFAULT_INQUIRY_ANSWER_CHARS, INQUIRY_STATE_TEXT_CHARS } from './consts.js';
2
- /**
3
- * Pure helpers over an inquiry and its answer — no IO, no state, no clock.
4
- *
5
- * They are here rather than in the runtime because every layer needs the same reading of an
6
- * answer: the execution service, the pipeline runner, the `ask_user` tool, the connector and the
7
- * screen that finally shows it. A second reading of "was this answered" is a second contract.
8
- */
9
- /**
10
- * What to assume when nobody answers: the question's own default, or a decline.
11
- *
12
- * A decline is an ANSWER — the run carries on and records what it assumed — which is why this
13
- * never throws and never returns null.
14
- */
15
- export const defaultAnswerFor = (inquiry) => inquiry.default != null
16
- ? { inquiryId: inquiry.id, value: inquiry.default }
17
- : { inquiryId: inquiry.id, declined: true };
18
- /**
19
- * The one thing an answer says, as a string — the first chosen value, else the free text, else
20
- * `null` when it says nothing at all (a decline, or an empty answer).
21
- */
22
- export const answeredWith = (answer) => {
23
- const value = Array.isArray(answer.value) ? answer.value[0] : answer.value;
24
- if (value != null && value !== '')
25
- return value;
26
- if (answer.text != null && answer.text !== '')
27
- return answer.text;
28
- return null;
29
- };
30
- /** Nobody could decide. Distinct from an empty answer — see {@link answeredWith}. */
31
- export const isDeclined = (answer) => answer.declined === true;
32
- /**
33
- * Cut an answer's free TEXT to the ceiling and SAY SO.
34
- *
35
- * Only the prose is cut. A `value` IS the decision — for a choice it has to equal one of the
36
- * question's own option values — so slicing one does not degrade an answer, it silently replaces
37
- * it with an identifier nobody offered and nothing matches, which `answeredWith` then hands on as
38
- * what the person chose. An over-long value is a defect upstream rather than a long answer (the
39
- * connector's schema refuses one outright instead of shortening it), so it travels on whole and is
40
- * REPORTED through `truncated` — the flag every consumer already reads as "this is not exactly
41
- * what the person gave".
42
- */
43
- export const capAnswer = (answer, max = DEFAULT_INQUIRY_ANSWER_CHARS) => {
44
- const text = answer.text != null && answer.text.length > max
45
- ? answer.text.slice(0, max)
46
- : undefined;
47
- const values = Array.isArray(answer.value)
48
- ? answer.value
49
- : answer.value != null ? [answer.value] : [];
50
- const oversized = values.some(value => value.length > max);
51
- if (text == null && !oversized)
52
- return answer;
53
- return { ...answer, ...(text != null ? { text } : {}), truncated: true };
54
- };
55
- /**
56
- * The copy of an answer a resumable pipeline STATE keeps: the decision whole, the prose cut to
57
- * {@link INQUIRY_STATE_TEXT_CHARS}.
58
- *
59
- * The full answer goes back to whoever asked; only this reduced one is persisted, so a run that
60
- * asks several questions still holds a state made of keys rather than of paragraphs.
61
- */
62
- export const stateAnswerOf = (answer) => answer.text != null && answer.text.length > INQUIRY_STATE_TEXT_CHARS
63
- ? { ...answer, text: answer.text.slice(0, INQUIRY_STATE_TEXT_CHARS), truncated: true }
64
- : answer;
65
- /** One-line label of a question — for a note, a trace line or a run row. */
66
- export const renderInquiry = (inquiry) => {
67
- const options = inquiry.options != null && inquiry.options.length > 0
68
- ? ` (${inquiry.options.map(option => option.value).join(' | ')})`
69
- : '';
70
- return `[${inquiry.kind}] ${inquiry.question}${options}`.replace(/\s+/g, ' ').trim();
71
- };
72
- //# sourceMappingURL=utils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/inquiry/utils.ts"],"names":[],"mappings":"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"}
@@ -1,52 +0,0 @@
1
- /**
2
- * What shape of answer a question expects. Deliberately three, because a question a person is
3
- * asked mid-run is answered in seconds or not at all: pick one of these, say it in your own
4
- * words, or say yes/no. Anything richer is a form, and a form belongs to an application screen.
5
- */
6
- export enum InquiryKind {
7
- Choice = 'choice',
8
- Text = 'text',
9
- Confirm = 'confirm',
10
- }
11
-
12
- /**
13
- * What a run is allowed to do when it needs a decision that is not its own.
14
- *
15
- * - `Ask` — put it to a person through the seated transport and wait.
16
- * - `Default` — assume the question's own default (or record a decline) and carry on. The caller
17
- * is expected to RECORD the assumption; a run nobody is watching must never block.
18
- * - `Refuse` — nobody may be asked at all; the attempt is an error.
19
- */
20
- export enum InquiryPolicy {
21
- Ask = 'ask',
22
- Default = 'default',
23
- Refuse = 'refuse',
24
- }
25
-
26
- /**
27
- * The ONE ceiling on a stored answer, in characters. An answer is a decision, not a document.
28
- *
29
- * Every layer that carries an answer references this constant rather than choosing its own:
30
- * `viable-common`'s `CONNECT_INQUIRY_MAX_TEXT` (the wire copy) equals it, `capAnswer` enforces it,
31
- * and the connector's answer schema caps `text` at it. Three ceilings for one value is how a
32
- * user's answer gets accepted on the wire and silently halved further in.
33
- */
34
- export const DEFAULT_INQUIRY_ANSWER_CHARS = 2_000
35
-
36
- /** How many options one question may offer. Beyond this it is not a question. */
37
- export const DEFAULT_INQUIRY_OPTIONS = 12
38
-
39
- /**
40
- * How much of an answer's free text a resumable pipeline STATE may hold.
41
- *
42
- * A pipeline state is keys, markers and paths — a couple of full-size answers would make it prose,
43
- * which is the invariant the whole pipeline design rests on. The decision (`value`/`declined`)
44
- * stays whole; the prose is cut here and belongs in whatever document the application keeps for
45
- * it (`stateAnswerOf`).
46
- */
47
- export const INQUIRY_STATE_TEXT_CHARS = 200
48
-
49
- /** The answer value of a confirmed {@link InquiryKind.Confirm}. */
50
- export const CONFIRM_YES = 'yes'
51
- /** The answer value of a refused {@link InquiryKind.Confirm}. */
52
- export const CONFIRM_NO = 'no'
@@ -1,3 +0,0 @@
1
- export * from './consts.js'
2
- export type * from './types.js'
3
- export * from './utils.js'
@@ -1,77 +0,0 @@
1
- import type { InquiryKind, InquiryPolicy } from './consts.js'
2
-
3
- /**
4
- * One question put to a person while a run is in flight, and the answer that comes back.
5
- *
6
- * Everything here is serializable for the same reason a delegated task is: whoever answers is
7
- * outside this process — a browser dialog, a coding agent driving the application through a
8
- * connector, a test double — and the question may outlive the process that asked it, parked on a
9
- * pipeline run row until somebody comes back to it.
10
- */
11
-
12
- export interface InquiryOption {
13
- value: string
14
- label: string
15
- description?: string
16
- }
17
-
18
- export interface Inquiry {
19
- /** Stable id, chosen by whoever asks. The ONLY thing that routes an answer back. */
20
- id: string
21
- kind: InquiryKind
22
- /** One question, in plain words. */
23
- question: string
24
- /** One or two sentences of background. Never the whole task. */
25
- context?: string
26
- /** Required for {@link InquiryKind.Choice}; ignored otherwise. */
27
- options?: InquiryOption[]
28
- multiple?: boolean
29
- /** A `Choice` the answerer may answer in their own words instead. */
30
- allowText?: boolean
31
- /** What to assume when nobody answers. {@link InquiryPolicy.Default} returns exactly this. */
32
- default?: string | string[]
33
- expiresAt?: string
34
- }
35
-
36
- export interface InquiryAnswer {
37
- inquiryId: string
38
- value?: string | string[]
39
- text?: string
40
- /** Nobody could decide. A legitimate ANSWER, never a failure. */
41
- declined?: boolean
42
- /**
43
- * This answer is not exactly the one that was given. Never an answerer's own flag.
44
- *
45
- * Two writers, two ceilings: `capAnswer` cuts the text to `DEFAULT_INQUIRY_ANSWER_CHARS` (and
46
- * raises this WITHOUT cutting when a `value` arrived over that ceiling, since a shortened
47
- * identifier matches no option), while `stateAnswerOf` cuts the text again to the much smaller
48
- * `INQUIRY_STATE_TEXT_CHARS` a pipeline state may hold. So a flag read back off a resumed run
49
- * says the state's copy is short — not that the person hit the answer ceiling.
50
- *
51
- * It exists so no cut is silent: whoever records the answer can say that the rest of it was
52
- * dropped, instead of the answerer discovering it in the work that followed.
53
- */
54
- truncated?: boolean
55
- }
56
-
57
- /**
58
- * How a question reaches a person.
59
- *
60
- * One method, like `DelegateTransport` beside it, and for the same reason: routing, waiting,
61
- * redelivery and giving up all belong to whoever implements it. A transport that cannot serve the
62
- * question must THROW rather than answer — a declined answer is a decision, while a channel that
63
- * is not there is terminal, and the two must never look alike.
64
- */
65
- export interface InquiryTransport {
66
- ask: (inquiry: Inquiry, signal?: AbortSignal) => Promise<InquiryAnswer>
67
- }
68
-
69
- /**
70
- * How one run may put a question to a person. Carried on an execution's serializable state, so a
71
- * resumed run keeps the channel and the policy it was started with.
72
- */
73
- export interface InquiryConfig {
74
- /** The key the application seated an {@link InquiryTransport} under. */
75
- transport?: string
76
- policy: InquiryPolicy
77
- }
@@ -1,84 +0,0 @@
1
- import { DEFAULT_INQUIRY_ANSWER_CHARS, INQUIRY_STATE_TEXT_CHARS } from './consts.js'
2
- import type { Inquiry, InquiryAnswer } from './types.js'
3
-
4
- /**
5
- * Pure helpers over an inquiry and its answer — no IO, no state, no clock.
6
- *
7
- * They are here rather than in the runtime because every layer needs the same reading of an
8
- * answer: the execution service, the pipeline runner, the `ask_user` tool, the connector and the
9
- * screen that finally shows it. A second reading of "was this answered" is a second contract.
10
- */
11
-
12
- /**
13
- * What to assume when nobody answers: the question's own default, or a decline.
14
- *
15
- * A decline is an ANSWER — the run carries on and records what it assumed — which is why this
16
- * never throws and never returns null.
17
- */
18
- export const defaultAnswerFor = (inquiry: Inquiry): InquiryAnswer =>
19
- inquiry.default != null
20
- ? { inquiryId: inquiry.id, value: inquiry.default }
21
- : { inquiryId: inquiry.id, declined: true }
22
-
23
- /**
24
- * The one thing an answer says, as a string — the first chosen value, else the free text, else
25
- * `null` when it says nothing at all (a decline, or an empty answer).
26
- */
27
- export const answeredWith = (answer: InquiryAnswer): string | null => {
28
- const value = Array.isArray(answer.value) ? answer.value[0] : answer.value
29
- if (value != null && value !== '') return value
30
- if (answer.text != null && answer.text !== '') return answer.text
31
-
32
- return null
33
- }
34
-
35
- /** Nobody could decide. Distinct from an empty answer — see {@link answeredWith}. */
36
- export const isDeclined = (answer: InquiryAnswer): boolean => answer.declined === true
37
-
38
- /**
39
- * Cut an answer's free TEXT to the ceiling and SAY SO.
40
- *
41
- * Only the prose is cut. A `value` IS the decision — for a choice it has to equal one of the
42
- * question's own option values — so slicing one does not degrade an answer, it silently replaces
43
- * it with an identifier nobody offered and nothing matches, which `answeredWith` then hands on as
44
- * what the person chose. An over-long value is a defect upstream rather than a long answer (the
45
- * connector's schema refuses one outright instead of shortening it), so it travels on whole and is
46
- * REPORTED through `truncated` — the flag every consumer already reads as "this is not exactly
47
- * what the person gave".
48
- */
49
- export const capAnswer = (
50
- answer: InquiryAnswer, max: number = DEFAULT_INQUIRY_ANSWER_CHARS
51
- ): InquiryAnswer => {
52
- const text = answer.text != null && answer.text.length > max
53
- ? answer.text.slice(0, max)
54
- : undefined
55
- const values = Array.isArray(answer.value)
56
- ? answer.value
57
- : answer.value != null ? [answer.value] : []
58
- const oversized = values.some(value => value.length > max)
59
-
60
- if (text == null && !oversized) return answer
61
-
62
- return { ...answer, ...(text != null ? { text } : {}), truncated: true }
63
- }
64
-
65
- /**
66
- * The copy of an answer a resumable pipeline STATE keeps: the decision whole, the prose cut to
67
- * {@link INQUIRY_STATE_TEXT_CHARS}.
68
- *
69
- * The full answer goes back to whoever asked; only this reduced one is persisted, so a run that
70
- * asks several questions still holds a state made of keys rather than of paragraphs.
71
- */
72
- export const stateAnswerOf = (answer: InquiryAnswer): InquiryAnswer =>
73
- answer.text != null && answer.text.length > INQUIRY_STATE_TEXT_CHARS
74
- ? { ...answer, text: answer.text.slice(0, INQUIRY_STATE_TEXT_CHARS), truncated: true }
75
- : answer
76
-
77
- /** One-line label of a question — for a note, a trace line or a run row. */
78
- export const renderInquiry = (inquiry: Inquiry): string => {
79
- const options = inquiry.options != null && inquiry.options.length > 0
80
- ? ` (${inquiry.options.map(option => option.value).join(' | ')})`
81
- : ''
82
-
83
- return `[${inquiry.kind}] ${inquiry.question}${options}`.replace(/\s+/g, ' ').trim()
84
- }
@@ -1,112 +0,0 @@
1
- import { describe, expect, test } from 'bun:test'
2
- import {
3
- answeredWith, capAnswer, CONFIRM_NO, CONFIRM_YES, defaultAnswerFor, DEFAULT_INQUIRY_ANSWER_CHARS,
4
- INQUIRY_STATE_TEXT_CHARS, InquiryKind, isDeclined, renderInquiry, stateAnswerOf,
5
- } from '../src/index.js'
6
- import type { Inquiry, InquiryAnswer } from '../src/index.js'
7
-
8
- /**
9
- * The pure half of the inquiry primitive. Every layer that carries an answer — the execution
10
- * service, the pipeline runner, the `ask_user` tool, the connector — reads it through exactly
11
- * these functions, so a second reading of "was this answered" is what these specs exist to stop.
12
- */
13
-
14
- const inquiry = (patch: Partial<Inquiry> = {}): Inquiry => ({
15
- id: 'q1',
16
- kind: InquiryKind.Choice,
17
- question: 'Which database does the origin use?',
18
- options: [
19
- { value: 'postgres', label: 'PostgreSQL' },
20
- { value: 'mongo', label: 'MongoDB' },
21
- ],
22
- ...patch,
23
- })
24
-
25
- describe('@owlmeans/llm-common — what an unanswered question assumes', () => {
26
- test('a question with a default answers itself with it', () => {
27
- expect(defaultAnswerFor(inquiry({ default: 'postgres' })))
28
- .toEqual({ inquiryId: 'q1', value: 'postgres' })
29
- })
30
-
31
- test('a question without one declines — a decision, never a failure', () => {
32
- const answer = defaultAnswerFor(inquiry())
33
- expect(isDeclined(answer)).toBe(true)
34
- expect(answeredWith(answer)).toBeNull()
35
- })
36
-
37
- test('a multiple-choice default travels whole', () => {
38
- expect(defaultAnswerFor(inquiry({ multiple: true, default: ['postgres', 'mongo'] })).value)
39
- .toEqual(['postgres', 'mongo'])
40
- })
41
- })
42
-
43
- describe('@owlmeans/llm-common — reading one answer', () => {
44
- test('the chosen value wins over free text, and the first of a list is the answer', () => {
45
- expect(answeredWith({ inquiryId: 'q1', value: 'mongo', text: 'or postgres' })).toBe('mongo')
46
- expect(answeredWith({ inquiryId: 'q1', value: ['mongo', 'redis'] })).toBe('mongo')
47
- })
48
-
49
- test('free text answers a question that offered no options', () => {
50
- expect(answeredWith({ inquiryId: 'q1', text: 'the vendor one' })).toBe('the vendor one')
51
- })
52
-
53
- test('an answer that says nothing reads as nothing, declined or not', () => {
54
- expect(answeredWith({ inquiryId: 'q1' })).toBeNull()
55
- expect(answeredWith({ inquiryId: 'q1', value: '', text: '' })).toBeNull()
56
- expect(isDeclined({ inquiryId: 'q1' })).toBe(false)
57
- expect(isDeclined({ inquiryId: 'q1', declined: true })).toBe(true)
58
- })
59
-
60
- test('a confirmation is just its two values', () => {
61
- expect(answeredWith({ inquiryId: 'q1', value: CONFIRM_YES })).toBe(CONFIRM_YES)
62
- expect(answeredWith({ inquiryId: 'q1', value: CONFIRM_NO })).toBe(CONFIRM_NO)
63
- })
64
- })
65
-
66
- describe('@owlmeans/llm-common — the one ceiling', () => {
67
- test('an answer inside the ceiling is left exactly as it came', () => {
68
- const answer: InquiryAnswer = { inquiryId: 'q1', value: 'mongo', text: 'because of the driver' }
69
- expect(capAnswer(answer)).toEqual(answer)
70
- expect(capAnswer(answer).truncated).toBeUndefined()
71
- })
72
-
73
- test('a cut is never silent', () => {
74
- const capped = capAnswer({ inquiryId: 'q1', text: 'x'.repeat(DEFAULT_INQUIRY_ANSWER_CHARS + 5) })
75
- expect(capped.text).toHaveLength(DEFAULT_INQUIRY_ANSWER_CHARS)
76
- expect(capped.truncated).toBe(true)
77
- })
78
-
79
- test('an over-long value is reported, never shortened into an option nobody offered', () => {
80
- const value = ['ok', 'y'.repeat(40)]
81
- const capped = capAnswer({ inquiryId: 'q1', value }, 10)
82
- // Cutting prose degrades an answer; cutting an identifier CHANGES it — `answeredWith` would
83
- // then hand the caller a choice the question never carried.
84
- expect(capped.value).toEqual(value)
85
- expect(capped.truncated).toBe(true)
86
- })
87
-
88
- test('the state copy keeps the decision whole and cuts only the prose', () => {
89
- const answer: InquiryAnswer = {
90
- inquiryId: 'q1', value: 'postgres', text: 'z'.repeat(INQUIRY_STATE_TEXT_CHARS + 100),
91
- }
92
- const stored = stateAnswerOf(answer)
93
- expect(stored.value).toBe('postgres')
94
- expect(stored.text).toHaveLength(INQUIRY_STATE_TEXT_CHARS)
95
- expect(stored.truncated).toBe(true)
96
- // The answer handed back to whoever asked is untouched — only the persisted copy is reduced.
97
- expect(answer.text).toHaveLength(INQUIRY_STATE_TEXT_CHARS + 100)
98
- expect(stateAnswerOf({ inquiryId: 'q1', value: 'postgres' }).truncated).toBeUndefined()
99
- })
100
- })
101
-
102
- describe('@owlmeans/llm-common — one line for a trace', () => {
103
- test('the label names the kind, the question and the choices, on one line', () => {
104
- expect(renderInquiry(inquiry({ question: 'Which\n database?' })))
105
- .toBe('[choice] Which database? (postgres | mongo)')
106
- })
107
-
108
- test('a question with no options renders without an empty bracket', () => {
109
- expect(renderInquiry({ id: 'q2', kind: InquiryKind.Confirm, question: 'Relocate the origin?' }))
110
- .toBe('[confirm] Relocate the origin?')
111
- })
112
- })