@try-works/dsh-model-as-tool 0.1.2 → 0.1.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.
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts +16 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.js +85 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts +20 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.js +112 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts +82 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.js +260 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts +23 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.js +24 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts +104 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.js +55 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts +81 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.js +163 -0
- package/node_modules/@try-works/tool-mode-contracts/package.json +35 -0
- package/package.json +10 -5
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `text.language_detect` — the language-identification contract (TOOL-03).
|
|
3
|
+
*
|
|
4
|
+
* TOOL-03 requires three things a naive wrapper loses: ambiguity must survive to
|
|
5
|
+
* the caller, empty input has a defined answer rather than an exception, and the
|
|
6
|
+
* score's MEANING is declared. It also forbids a specific mistake worth naming in
|
|
7
|
+
* the schema: a text language detector is not evidence about the language spoken
|
|
8
|
+
* in audio it never processed, so the contract says so instead of leaving a
|
|
9
|
+
* caller to infer capability from a similar-looking tool.
|
|
10
|
+
*
|
|
11
|
+
* @module @try-works/tool-mode-contracts/contracts/lang-detect
|
|
12
|
+
*/
|
|
13
|
+
import type { ToolContract } from '../registry.ts';
|
|
14
|
+
/** The `text.language_detect` contract at version 1.0.0. */
|
|
15
|
+
export declare const langDetect: ToolContract;
|
|
16
|
+
//# sourceMappingURL=lang-detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lang-detect.d.ts","sourceRoot":"","sources":["../../src/contracts/lang-detect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAElD,4DAA4D;AAC5D,eAAO,MAAM,UAAU,EAAE,YA8ExB,CAAA"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `text.language_detect` — the language-identification contract (TOOL-03).
|
|
3
|
+
*
|
|
4
|
+
* TOOL-03 requires three things a naive wrapper loses: ambiguity must survive to
|
|
5
|
+
* the caller, empty input has a defined answer rather than an exception, and the
|
|
6
|
+
* score's MEANING is declared. It also forbids a specific mistake worth naming in
|
|
7
|
+
* the schema: a text language detector is not evidence about the language spoken
|
|
8
|
+
* in audio it never processed, so the contract says so instead of leaving a
|
|
9
|
+
* caller to infer capability from a similar-looking tool.
|
|
10
|
+
*
|
|
11
|
+
* @module @try-works/tool-mode-contracts/contracts/lang-detect
|
|
12
|
+
*/
|
|
13
|
+
import { withMeta } from "../meta.js";
|
|
14
|
+
/** The `text.language_detect` contract at version 1.0.0. */
|
|
15
|
+
export const langDetect = {
|
|
16
|
+
contract: 'text.language_detect',
|
|
17
|
+
version: '1.0.0',
|
|
18
|
+
toolName: 'lang_detect',
|
|
19
|
+
description: 'Identify the language of a short piece of text. Returns the best language, a reliability '
|
|
20
|
+
+ 'verdict and ranked alternatives, because short text is often genuinely ambiguous between '
|
|
21
|
+
+ 'related languages and collapsing that to one answer would overstate the result.',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
additionalProperties: false,
|
|
25
|
+
required: ['text'],
|
|
26
|
+
properties: {
|
|
27
|
+
text: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
minLength: 1,
|
|
30
|
+
maxLength: 4096,
|
|
31
|
+
description: 'The text to identify. Only the leading portion is examined.',
|
|
32
|
+
},
|
|
33
|
+
top: {
|
|
34
|
+
type: 'integer',
|
|
35
|
+
minimum: 1,
|
|
36
|
+
maximum: 20,
|
|
37
|
+
default: 3,
|
|
38
|
+
description: 'How many ranked alternatives to return.',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
outputSchema: withMeta({
|
|
43
|
+
language: { type: ['string', 'null'], description: 'Best candidate, or null when input had no language signal.' },
|
|
44
|
+
reliability: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
enum: ['confident', 'likely', 'tentative', 'unknown'],
|
|
47
|
+
description: 'Vendor-reported verdict. Not a calibrated probability.',
|
|
48
|
+
},
|
|
49
|
+
tooCloseToCall: {
|
|
50
|
+
type: 'boolean',
|
|
51
|
+
description: 'True when the leading candidates are within the implementation\'s declared tie margin.',
|
|
52
|
+
},
|
|
53
|
+
candidates: {
|
|
54
|
+
type: 'array',
|
|
55
|
+
items: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
required: ['language', 'probability'],
|
|
59
|
+
properties: {
|
|
60
|
+
language: { type: 'string' },
|
|
61
|
+
probability: { type: 'number', minimum: 0, maximum: 1 },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
}, ['reliability', 'tooCloseToCall', 'candidates']),
|
|
66
|
+
semantics: {
|
|
67
|
+
scoreKind: 'vendor-reported-probability',
|
|
68
|
+
calibrated: false,
|
|
69
|
+
ambiguityPreserved: true,
|
|
70
|
+
emptyInputMeaning: 'null-language-with-empty-candidates',
|
|
71
|
+
appliesTo: 'written-text-only',
|
|
72
|
+
},
|
|
73
|
+
limits: {
|
|
74
|
+
maxInputCharacters: 4096,
|
|
75
|
+
examinedCharacters: 512,
|
|
76
|
+
maxCandidates: 20,
|
|
77
|
+
},
|
|
78
|
+
unsupported: [
|
|
79
|
+
'Spoken language: this reads written text only. It is not evidence about the language spoken in audio it has not processed (TOOL-03).',
|
|
80
|
+
'Translation or transliteration.',
|
|
81
|
+
'Confident single answers for one or two words: those are frequently ambiguous and are reported as such.',
|
|
82
|
+
'Documents beyond the examined prefix: only the leading characters are considered.',
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=lang-detect.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `stt.transcribe` — the canonical transcription contract (TOOL-01).
|
|
3
|
+
*
|
|
4
|
+
* This contract is model-free on purpose. Any conforming implementation binds to
|
|
5
|
+
* it, so a caller addresses `stt_transcribe` once and the host decides which
|
|
6
|
+
* model runs (BASE-02). Adding a second transcription model must not add a
|
|
7
|
+
* second tool.
|
|
8
|
+
*
|
|
9
|
+
* The semantics block is the part a schema cannot express and the part the spec
|
|
10
|
+
* insists on (DECL-01, TOOL-01): whether this transcribes or translates, what
|
|
11
|
+
* the timestamps mean, and what an empty result means.
|
|
12
|
+
*
|
|
13
|
+
* @module @try-works/tool-mode-contracts/contracts/stt-transcribe
|
|
14
|
+
*/
|
|
15
|
+
import type { ToolContract } from '../registry.ts';
|
|
16
|
+
/** Accepted audio container/codec combinations for the bounded decoder. */
|
|
17
|
+
export declare const STT_ADMITTED_FORMATS: readonly ["wav-pcm16"];
|
|
18
|
+
/** The `stt.transcribe` contract at version 1.0.0. */
|
|
19
|
+
export declare const sttTranscribe: ToolContract;
|
|
20
|
+
//# sourceMappingURL=stt-transcribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stt-transcribe.d.ts","sourceRoot":"","sources":["../../src/contracts/stt-transcribe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAElD,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,wBAAyB,CAAA;AAE1D,sDAAsD;AACtD,eAAO,MAAM,aAAa,EAAE,YAqG3B,CAAA"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `stt.transcribe` — the canonical transcription contract (TOOL-01).
|
|
3
|
+
*
|
|
4
|
+
* This contract is model-free on purpose. Any conforming implementation binds to
|
|
5
|
+
* it, so a caller addresses `stt_transcribe` once and the host decides which
|
|
6
|
+
* model runs (BASE-02). Adding a second transcription model must not add a
|
|
7
|
+
* second tool.
|
|
8
|
+
*
|
|
9
|
+
* The semantics block is the part a schema cannot express and the part the spec
|
|
10
|
+
* insists on (DECL-01, TOOL-01): whether this transcribes or translates, what
|
|
11
|
+
* the timestamps mean, and what an empty result means.
|
|
12
|
+
*
|
|
13
|
+
* @module @try-works/tool-mode-contracts/contracts/stt-transcribe
|
|
14
|
+
*/
|
|
15
|
+
import { withMeta } from "../meta.js";
|
|
16
|
+
/** Accepted audio container/codec combinations for the bounded decoder. */
|
|
17
|
+
export const STT_ADMITTED_FORMATS = ['wav-pcm16'];
|
|
18
|
+
/** The `stt.transcribe` contract at version 1.0.0. */
|
|
19
|
+
export const sttTranscribe = {
|
|
20
|
+
contract: 'stt.transcribe',
|
|
21
|
+
version: '1.0.0',
|
|
22
|
+
toolName: 'stt_transcribe',
|
|
23
|
+
description: 'Transcribe a local audio recording into text with optional word timestamps. '
|
|
24
|
+
+ 'Runs on device by default; the caller selects no model. Accepts 16 kHz mono PCM16 WAV '
|
|
25
|
+
+ 'directly, and other encodings only through a tested decoder. Reports partial coverage '
|
|
26
|
+
+ 'rather than presenting an incomplete clip as fully transcribed.',
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
required: ['audio'],
|
|
31
|
+
properties: {
|
|
32
|
+
audio: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
additionalProperties: false,
|
|
35
|
+
required: ['path'],
|
|
36
|
+
properties: {
|
|
37
|
+
path: { type: 'string', minLength: 1, description: 'Absolute path to the recording.' },
|
|
38
|
+
sampleRateHz: {
|
|
39
|
+
type: 'integer',
|
|
40
|
+
minimum: 8000,
|
|
41
|
+
maximum: 48000,
|
|
42
|
+
description: 'Declared sample rate; inspected against the file header, not trusted.',
|
|
43
|
+
},
|
|
44
|
+
channels: { type: 'integer', minimum: 1, maximum: 2 },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
language: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
minLength: 2,
|
|
50
|
+
description: 'Requested language as a BCP-47/ISO 639 code. An unsupported request is an error, never a silent fallback.',
|
|
51
|
+
},
|
|
52
|
+
timestamps: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
enum: ['none', 'word'],
|
|
55
|
+
default: 'none',
|
|
56
|
+
description: 'Word timestamps when requested; an implementation that cannot supply them must error rather than omit them.',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
outputSchema: withMeta({
|
|
61
|
+
text: { type: 'string' },
|
|
62
|
+
coverage: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
required: ['complete', 'transcribedSeconds'],
|
|
66
|
+
properties: {
|
|
67
|
+
complete: { type: 'boolean' },
|
|
68
|
+
transcribedSeconds: { type: 'number', minimum: 0 },
|
|
69
|
+
totalSeconds: { type: ['number', 'null'], minimum: 0 },
|
|
70
|
+
reason: { type: ['string', 'null'] },
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
words: {
|
|
74
|
+
type: ['array', 'null'],
|
|
75
|
+
items: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
required: ['text', 'start', 'end'],
|
|
79
|
+
properties: {
|
|
80
|
+
text: { type: 'string' },
|
|
81
|
+
start: { type: 'number', minimum: 0, description: 'Seconds from the start of the audio.' },
|
|
82
|
+
end: { type: 'number', minimum: 0 },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
language: { type: ['string', 'null'], description: 'The language actually recognized, when the implementation reports it.' },
|
|
87
|
+
}, ['text', 'coverage']),
|
|
88
|
+
semantics: {
|
|
89
|
+
operation: 'transcription',
|
|
90
|
+
translation: false,
|
|
91
|
+
timestampUnit: 'seconds',
|
|
92
|
+
timestampReference: 'start-of-audio',
|
|
93
|
+
emptyResultMeaning: 'valid-empty-transcript',
|
|
94
|
+
partialResultMeaning: 'coverage.complete is false; the transcript is incomplete, not wrong',
|
|
95
|
+
languageMismatch: 'error',
|
|
96
|
+
unsupportedTimestampRequest: 'error',
|
|
97
|
+
},
|
|
98
|
+
limits: {
|
|
99
|
+
maxAudioSeconds: 3600,
|
|
100
|
+
maxAudioBytes: 512 * 1024 * 1024,
|
|
101
|
+
admittedSampleRateHz: 16000,
|
|
102
|
+
admittedChannels: 1,
|
|
103
|
+
},
|
|
104
|
+
unsupported: [
|
|
105
|
+
'Encoding without a tested decoder: only wav-pcm16 is admitted directly; assume nothing from the file extension.',
|
|
106
|
+
'Translation: this contract transcribes, it does not translate.',
|
|
107
|
+
'Diarization or speaker attribution: separate capability.',
|
|
108
|
+
'Timestamps when the selected implementation cannot produce them: reported as an error.',
|
|
109
|
+
'Live/streaming audio: the input is a completed local file.',
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=stt-transcribe.js.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection of a contract into DSH's parameter DSL (DECL-01).
|
|
3
|
+
*
|
|
4
|
+
* DECL-01 requires that DSH registration, MCP projection and Code Mode types all
|
|
5
|
+
* come from one source. The contract's JSON Schema is that source; this module is
|
|
6
|
+
* the DSH-specific projection of it.
|
|
7
|
+
*
|
|
8
|
+
* It replaces the old registrar behaviour that inferred the required input from
|
|
9
|
+
* parameter ORDER (BASE-04). Requiredness now comes from the schema's `required`
|
|
10
|
+
* array, so a contract cannot silently change meaning when a property is
|
|
11
|
+
* reordered.
|
|
12
|
+
*
|
|
13
|
+
* @module @try-works/tool-mode-contracts/dsh
|
|
14
|
+
*/
|
|
15
|
+
import type { ToolContract } from './registry.ts';
|
|
16
|
+
/** One DSH parameter property: a JSON-Schema subset plus `required`. */
|
|
17
|
+
type DshProperty = Record<string, unknown> & {
|
|
18
|
+
required?: true;
|
|
19
|
+
};
|
|
20
|
+
/** The implicit-open-object parameter map DSH expects. */
|
|
21
|
+
export type DshParameters = Record<string, DshProperty>;
|
|
22
|
+
/** The result of projecting a contract into DSH's narrower DSL. */
|
|
23
|
+
export interface DshProjection {
|
|
24
|
+
/** What the model-facing parameter schema advertises. */
|
|
25
|
+
readonly parameters: DshParameters;
|
|
26
|
+
/**
|
|
27
|
+
* Contract keywords the parameter schema cannot advertise, in `path.keyword`
|
|
28
|
+
* form. They remain enforced at runtime against the full contract schema.
|
|
29
|
+
*/
|
|
30
|
+
readonly runtimeEnforced: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
/** Raised when a contract uses a schema shape the DSH DSL cannot express. */
|
|
33
|
+
export declare class ProjectionError extends Error {
|
|
34
|
+
readonly name = "ProjectionError";
|
|
35
|
+
readonly unsupported: readonly string[];
|
|
36
|
+
constructor(contract: string, unsupported: readonly string[]);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Project a contract's input schema into DSH parameters.
|
|
40
|
+
*
|
|
41
|
+
* DSH's DSL is narrower than JSON Schema — it has no `minLength`, `minimum` or
|
|
42
|
+
* `pattern` at any nesting level — so the projection is deliberately lossy in what
|
|
43
|
+
* it ADVERTISES while remaining complete in what it ENFORCES. BASE-04 forbids
|
|
44
|
+
* silently discarding a constraint, so every keyword left out is returned in
|
|
45
|
+
* {@link DshProjection.runtimeEnforced} and is checked by `assertValidInput`
|
|
46
|
+
* against the untouched contract.
|
|
47
|
+
* @param contract - the contract to project.
|
|
48
|
+
* @returns the parameter map plus the keywords enforced only at runtime.
|
|
49
|
+
*/
|
|
50
|
+
export declare function projectToDsh(contract: ToolContract): DshProjection;
|
|
51
|
+
/**
|
|
52
|
+
* Project a contract's input schema into DSH parameters.
|
|
53
|
+
* @param contract - the contract to project.
|
|
54
|
+
* @returns the parameter map.
|
|
55
|
+
* @throws ProjectionError when the schema is internally inconsistent.
|
|
56
|
+
*/
|
|
57
|
+
export declare function toDshParameters(contract: ToolContract): DshParameters;
|
|
58
|
+
/**
|
|
59
|
+
* Cross-field constraints the DSH parameter DSL cannot express (BASE-04).
|
|
60
|
+
*
|
|
61
|
+
* Each returns a violation message, or undefined when the arguments are
|
|
62
|
+
* acceptable. They run after schema validation, so each may assume its fields
|
|
63
|
+
* are present and correctly typed.
|
|
64
|
+
*/
|
|
65
|
+
export type CrossFieldRule = (args: Record<string, unknown>) => string | undefined;
|
|
66
|
+
/** The cross-field rules that apply to a given contract. */
|
|
67
|
+
export declare function crossFieldRules(contract: ToolContract): CrossFieldRule[];
|
|
68
|
+
/**
|
|
69
|
+
* Project an arbitrary JSON Schema into the subset DSH can advertise (DECL-02).
|
|
70
|
+
*
|
|
71
|
+
* Written for the OUTPUT schema, which needs the same treatment the input schema
|
|
72
|
+
* already gets. Using the contract's raw output schema would crash registration on the
|
|
73
|
+
* first keyword the DSL cannot express — the failure that produced `runtimeEnforced` in
|
|
74
|
+
* the first place — and using an unconstrained `{type: "object", additionalProperties:
|
|
75
|
+
* true}` instead, which is what this replaced, publishes a tool whose result shape is
|
|
76
|
+
* declared to be anything at all.
|
|
77
|
+
* @param schema - the schema to project.
|
|
78
|
+
* @returns the projected schema plus the keywords left to runtime enforcement.
|
|
79
|
+
*/
|
|
80
|
+
export declare function projectSchemaToDsh(schema: Readonly<Record<string, unknown>>): DshProjection;
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=dsh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsh.d.ts","sourceRoot":"","sources":["../src/dsh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,wEAAwE;AACxE,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,IAAI,CAAA;CAAE,CAAA;AAEhE,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;AAEvD,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAA;IAClC;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5C;AAED,6EAA6E;AAC7E,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAkB,IAAI,qBAAoB;IAC1C,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;gBAC3B,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE;CAO7D;AAyFD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,aAAa,CAuBlE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,aAAa,CAErE;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,GAAG,SAAS,CAAA;AAElF,4DAA4D;AAC5D,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,cAAc,EAAE,CAqCxE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,aAAa,CAsB3F"}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection of a contract into DSH's parameter DSL (DECL-01).
|
|
3
|
+
*
|
|
4
|
+
* DECL-01 requires that DSH registration, MCP projection and Code Mode types all
|
|
5
|
+
* come from one source. The contract's JSON Schema is that source; this module is
|
|
6
|
+
* the DSH-specific projection of it.
|
|
7
|
+
*
|
|
8
|
+
* It replaces the old registrar behaviour that inferred the required input from
|
|
9
|
+
* parameter ORDER (BASE-04). Requiredness now comes from the schema's `required`
|
|
10
|
+
* array, so a contract cannot silently change meaning when a property is
|
|
11
|
+
* reordered.
|
|
12
|
+
*
|
|
13
|
+
* @module @try-works/tool-mode-contracts/dsh
|
|
14
|
+
*/
|
|
15
|
+
/** Raised when a contract uses a schema shape the DSH DSL cannot express. */
|
|
16
|
+
export class ProjectionError extends Error {
|
|
17
|
+
name = 'ProjectionError';
|
|
18
|
+
unsupported;
|
|
19
|
+
constructor(contract, unsupported) {
|
|
20
|
+
super(`${contract} cannot be projected into the DSH parameter DSL: ${unsupported.join('; ')}. `
|
|
21
|
+
+ 'Weakening the schema silently is not permitted (BASE-04), so the contract must be reshaped.');
|
|
22
|
+
this.unsupported = unsupported;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* JSON-Schema keywords the DSH value-schema DSL can carry.
|
|
27
|
+
*
|
|
28
|
+
* Anything outside this set is not DISCARDED: it stays in the contract and is
|
|
29
|
+
* enforced at runtime by {@link assertValidInput}, which validates arguments
|
|
30
|
+
* against the full contract schema before the implementation runs. This module
|
|
31
|
+
* only decides what the model-facing parameter schema advertises.
|
|
32
|
+
*/
|
|
33
|
+
const DSH_SUPPORTED = new Set([
|
|
34
|
+
'type', 'enum', 'const', 'description', 'default', 'examples',
|
|
35
|
+
'items', 'properties', 'additionalProperties', 'required',
|
|
36
|
+
]);
|
|
37
|
+
/**
|
|
38
|
+
* Project one JSON-Schema node into the subset DSH can carry.
|
|
39
|
+
*
|
|
40
|
+
* Recursive because DSH validates nested object properties too, so a constraint
|
|
41
|
+
* on `audio.path` reaches the same DSL limit as one on a top-level property.
|
|
42
|
+
* @param node - the schema node to project.
|
|
43
|
+
* @param path - dotted path for reporting what was left out.
|
|
44
|
+
* @param runtimeEnforced - accumulates `path.keyword` entries.
|
|
45
|
+
* @returns the projected node.
|
|
46
|
+
*/
|
|
47
|
+
function projectNode(node, path, runtimeEnforced) {
|
|
48
|
+
const out = {};
|
|
49
|
+
/**
|
|
50
|
+
* Requiredness is expressed PER PROPERTY, at every nesting level.
|
|
51
|
+
*
|
|
52
|
+
* DSH's value schema DSL does not admit a `required` array anywhere — it rejected
|
|
53
|
+
* `schema.required` at the root and `items.required` inside an array — so the list is
|
|
54
|
+
* folded into `required: true` on each named property, which the DSL does understand.
|
|
55
|
+
* Doing it here rather than at the root is what makes it work at depth: an output
|
|
56
|
+
* schema's array items have required fields of their own.
|
|
57
|
+
*/
|
|
58
|
+
const requiredNames = new Set(node.required ?? []);
|
|
59
|
+
for (const [keyword, value] of Object.entries(node)) {
|
|
60
|
+
if (!DSH_SUPPORTED.has(keyword)) {
|
|
61
|
+
runtimeEnforced.push(`${path}.${keyword}`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Handled through `properties` above; the DSL has no array form.
|
|
65
|
+
if (keyword === 'required')
|
|
66
|
+
continue;
|
|
67
|
+
if (keyword === 'properties' && typeof value === 'object' && value !== null) {
|
|
68
|
+
const nested = {};
|
|
69
|
+
for (const [name, child] of Object.entries(value)) {
|
|
70
|
+
const projected = typeof child === 'object' && child !== null
|
|
71
|
+
? projectNode(child, `${path}.${name}`, runtimeEnforced)
|
|
72
|
+
: child;
|
|
73
|
+
nested[name] = requiredNames.has(name) && typeof projected === 'object' && projected !== null
|
|
74
|
+
? { ...projected, required: true }
|
|
75
|
+
: projected;
|
|
76
|
+
}
|
|
77
|
+
out[keyword] = nested;
|
|
78
|
+
}
|
|
79
|
+
else if (keyword === 'items' && typeof value === 'object' && value !== null) {
|
|
80
|
+
out[keyword] = projectNode(value, `${path}[]`, runtimeEnforced);
|
|
81
|
+
}
|
|
82
|
+
else if (keyword === 'type' && Array.isArray(value)) {
|
|
83
|
+
/**
|
|
84
|
+
* A union `type` array advertises its CONCRETE member, with the union recorded as
|
|
85
|
+
* runtime-enforced.
|
|
86
|
+
*
|
|
87
|
+
* Three attempts got here. `{ type: ['string', 'null'] }` is valid JSON Schema but
|
|
88
|
+
* the DSL rejected the array with "type must be string/number/... or use oneOf",
|
|
89
|
+
* and converting it to `oneOf` was rejected too, with "oneOf must be an array of at
|
|
90
|
+
* least two value schemas" - `{ type: 'null' }` does not count as one. So the
|
|
91
|
+
* non-null member is advertised, which is the case a client actually handles, and
|
|
92
|
+
* the nullability is reported in runtimeEnforced rather than dropped silently,
|
|
93
|
+
* which is what BASE-04 forbids.
|
|
94
|
+
*/
|
|
95
|
+
const members = value;
|
|
96
|
+
const concrete = members.find(member => member !== 'null');
|
|
97
|
+
if (concrete === undefined) {
|
|
98
|
+
runtimeEnforced.push(`${path}.type`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
out.type = concrete;
|
|
102
|
+
if (members.length > 1)
|
|
103
|
+
runtimeEnforced.push(`${path}.type(non-nullable projection)`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
out[keyword] = value;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Project a contract's input schema into DSH parameters.
|
|
114
|
+
*
|
|
115
|
+
* DSH's DSL is narrower than JSON Schema — it has no `minLength`, `minimum` or
|
|
116
|
+
* `pattern` at any nesting level — so the projection is deliberately lossy in what
|
|
117
|
+
* it ADVERTISES while remaining complete in what it ENFORCES. BASE-04 forbids
|
|
118
|
+
* silently discarding a constraint, so every keyword left out is returned in
|
|
119
|
+
* {@link DshProjection.runtimeEnforced} and is checked by `assertValidInput`
|
|
120
|
+
* against the untouched contract.
|
|
121
|
+
* @param contract - the contract to project.
|
|
122
|
+
* @returns the parameter map plus the keywords enforced only at runtime.
|
|
123
|
+
*/
|
|
124
|
+
export function projectToDsh(contract) {
|
|
125
|
+
const schema = contract.inputSchema;
|
|
126
|
+
const properties = schema.properties ?? {};
|
|
127
|
+
const required = new Set(schema.required ?? []);
|
|
128
|
+
const runtimeEnforced = [];
|
|
129
|
+
const out = {};
|
|
130
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
131
|
+
const projected = projectNode(property, name, runtimeEnforced);
|
|
132
|
+
if (required.has(name))
|
|
133
|
+
projected.required = true;
|
|
134
|
+
out[name] = projected;
|
|
135
|
+
}
|
|
136
|
+
// A required property that is not declared cannot be satisfied by any caller.
|
|
137
|
+
const undeclared = [...required].filter(name => !(name in properties));
|
|
138
|
+
if (undeclared.length > 0) {
|
|
139
|
+
throw new ProjectionError(contract.contract, undeclared.map(name => `required property "${name}" is not declared`));
|
|
140
|
+
}
|
|
141
|
+
return { parameters: out, runtimeEnforced };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Project a contract's input schema into DSH parameters.
|
|
145
|
+
* @param contract - the contract to project.
|
|
146
|
+
* @returns the parameter map.
|
|
147
|
+
* @throws ProjectionError when the schema is internally inconsistent.
|
|
148
|
+
*/
|
|
149
|
+
export function toDshParameters(contract) {
|
|
150
|
+
return projectToDsh(contract).parameters;
|
|
151
|
+
}
|
|
152
|
+
/** The cross-field rules that apply to a given contract. */
|
|
153
|
+
export function crossFieldRules(contract) {
|
|
154
|
+
switch (contract.contract) {
|
|
155
|
+
case 'stt.transcribe':
|
|
156
|
+
return [
|
|
157
|
+
(args) => {
|
|
158
|
+
const timestamps = args.timestamps;
|
|
159
|
+
if (timestamps === 'word' && args.language === undefined) {
|
|
160
|
+
// Not a hard error, but a caller asking for word timing without
|
|
161
|
+
// declaring a language gets worse results on code-switched audio.
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
},
|
|
166
|
+
(args) => {
|
|
167
|
+
const audio = args.audio;
|
|
168
|
+
if (audio?.channels !== undefined && audio.channels !== 1) {
|
|
169
|
+
return 'audio.channels must be 1: the admitted input is 16 kHz mono';
|
|
170
|
+
}
|
|
171
|
+
if (audio?.sampleRateHz !== undefined && audio.sampleRateHz !== 16000) {
|
|
172
|
+
return 'audio.sampleRateHz must be 16000: resampling is not part of this contract';
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
case 'text.language_detect':
|
|
178
|
+
return [
|
|
179
|
+
(args) => {
|
|
180
|
+
const text = args.text;
|
|
181
|
+
if (typeof text === 'string' && text.trim().length === 0) {
|
|
182
|
+
return 'text must contain at least one non-whitespace character';
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
default:
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Project an arbitrary JSON Schema into the subset DSH can advertise (DECL-02).
|
|
193
|
+
*
|
|
194
|
+
* Written for the OUTPUT schema, which needs the same treatment the input schema
|
|
195
|
+
* already gets. Using the contract's raw output schema would crash registration on the
|
|
196
|
+
* first keyword the DSL cannot express — the failure that produced `runtimeEnforced` in
|
|
197
|
+
* the first place — and using an unconstrained `{type: "object", additionalProperties:
|
|
198
|
+
* true}` instead, which is what this replaced, publishes a tool whose result shape is
|
|
199
|
+
* declared to be anything at all.
|
|
200
|
+
* @param schema - the schema to project.
|
|
201
|
+
* @returns the projected schema plus the keywords left to runtime enforcement.
|
|
202
|
+
*/
|
|
203
|
+
export function projectSchemaToDsh(schema) {
|
|
204
|
+
const runtimeEnforced = [];
|
|
205
|
+
const projected = projectNode(schema, '', runtimeEnforced);
|
|
206
|
+
/**
|
|
207
|
+
* `items` is stripped from an OUTPUT projection, and this is a real DSL limit found
|
|
208
|
+
* the hard way rather than a precaution.
|
|
209
|
+
*
|
|
210
|
+
* `items` is in the supported set and works for a top-level input parameter, but the
|
|
211
|
+
* registry rejected `schema.properties.words.items` with "items is not supported by
|
|
212
|
+
* the value schema DSL" - so its support does not extend to an array nested inside an
|
|
213
|
+
* object, which is exactly what an output schema contains. Keeping it would make the
|
|
214
|
+
* tool fail to register at all.
|
|
215
|
+
*
|
|
216
|
+
* The result is still a genuine declaration: property names, types and requiredness
|
|
217
|
+
* are advertised, and `words` is declared an array. Only the ELEMENT schema is left to
|
|
218
|
+
* runtime enforcement, which the contract's assertValidOutput performs against the
|
|
219
|
+
* untouched schema. That is a long way from the
|
|
220
|
+
* `{type: "object", additionalProperties: true}` this replaced, which declared nothing
|
|
221
|
+
* about any field.
|
|
222
|
+
*/
|
|
223
|
+
const parameters = stripItems(projected.properties ?? {}, '', runtimeEnforced);
|
|
224
|
+
return { parameters: parameters, runtimeEnforced };
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Remove `items` at every depth in an output projection, recording each removal
|
|
228
|
+
* (DECL-02).
|
|
229
|
+
* @param node - the projected node.
|
|
230
|
+
* @param path - the path, for the runtime-enforced list.
|
|
231
|
+
* @param runtimeEnforced - collected keyword paths.
|
|
232
|
+
* @returns the node without `items`.
|
|
233
|
+
*/
|
|
234
|
+
function stripItems(node, path, runtimeEnforced) {
|
|
235
|
+
if (typeof node !== 'object' || node === null)
|
|
236
|
+
return node;
|
|
237
|
+
const out = {};
|
|
238
|
+
for (const [key, value] of Object.entries(node)) {
|
|
239
|
+
if (key === 'items') {
|
|
240
|
+
runtimeEnforced.push(`${path}.items`);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* A non-scalar `enum` is dropped, for the same reason `items` is.
|
|
245
|
+
*
|
|
246
|
+
* The DSL requires "a non-empty array of scalar values", and the confidence contract
|
|
247
|
+
* (DECL-04) enumerates OBJECT members to distinguish vendor-reported from calibrated
|
|
248
|
+
* from unavailable. Membership is still enforced at runtime against the untouched
|
|
249
|
+
* contract.
|
|
250
|
+
*/
|
|
251
|
+
const scalar = (member) => typeof member === 'string' || typeof member === 'number' || typeof member === 'boolean';
|
|
252
|
+
if (key === 'enum' && Array.isArray(value) && (value.length === 0 || !value.every(scalar))) {
|
|
253
|
+
runtimeEnforced.push(`${path}.enum`);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
out[key] = stripItems(value, `${path}.${key}`, runtimeEnforced);
|
|
257
|
+
}
|
|
258
|
+
return out;
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=dsh.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@try-works/tool-mode-contracts` — model-free standard tool contracts.
|
|
3
|
+
*
|
|
4
|
+
* This package contains no model bindings, no weights and no host code. It is the
|
|
5
|
+
* shared vocabulary that the Tool Mode host registers, that MCP clients read, and
|
|
6
|
+
* that extension packages implement against (DECL-01).
|
|
7
|
+
*
|
|
8
|
+
* @module @try-works/tool-mode-contracts
|
|
9
|
+
*/
|
|
10
|
+
export { assertValidContract, assertValidInput, assertValidOutput, ContractError, ContractViolationError, TOOL_META_SCHEMA, type ToolContract, } from './registry.ts';
|
|
11
|
+
export { withMeta, type ExecutionLocation, type ToolMeta } from './meta.ts';
|
|
12
|
+
export { crossFieldRules, ProjectionError, projectSchemaToDsh, projectToDsh, toDshParameters, type CrossFieldRule, type DshParameters, type DshProjection, } from './dsh.ts';
|
|
13
|
+
export { sttTranscribe, STT_ADMITTED_FORMATS } from './contracts/stt-transcribe.ts';
|
|
14
|
+
export { langDetect } from './contracts/lang-detect.ts';
|
|
15
|
+
import type { ToolContract } from './registry.ts';
|
|
16
|
+
/**
|
|
17
|
+
* The standard contracts every Tool Mode host knows how to register.
|
|
18
|
+
*
|
|
19
|
+
* A host iterates this list; it does not switch on model names, which is what
|
|
20
|
+
* ARCH-02 and EXT-01 require of model-independent code.
|
|
21
|
+
*/
|
|
22
|
+
export declare const STANDARD_CONTRACTS: readonly ToolContract[];
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,QAAQ,EAAE,KAAK,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAA;AAE3E,OAAO,EACL,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAIvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,YAAY,EAAgC,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@try-works/tool-mode-contracts` — model-free standard tool contracts.
|
|
3
|
+
*
|
|
4
|
+
* This package contains no model bindings, no weights and no host code. It is the
|
|
5
|
+
* shared vocabulary that the Tool Mode host registers, that MCP clients read, and
|
|
6
|
+
* that extension packages implement against (DECL-01).
|
|
7
|
+
*
|
|
8
|
+
* @module @try-works/tool-mode-contracts
|
|
9
|
+
*/
|
|
10
|
+
export { assertValidContract, assertValidInput, assertValidOutput, ContractError, ContractViolationError, TOOL_META_SCHEMA, } from "./registry.js";
|
|
11
|
+
export { withMeta } from "./meta.js";
|
|
12
|
+
export { crossFieldRules, ProjectionError, projectSchemaToDsh, projectToDsh, toDshParameters, } from "./dsh.js";
|
|
13
|
+
export { sttTranscribe, STT_ADMITTED_FORMATS } from "./contracts/stt-transcribe.js";
|
|
14
|
+
export { langDetect } from "./contracts/lang-detect.js";
|
|
15
|
+
import { sttTranscribe } from "./contracts/stt-transcribe.js";
|
|
16
|
+
import { langDetect } from "./contracts/lang-detect.js";
|
|
17
|
+
/**
|
|
18
|
+
* The standard contracts every Tool Mode host knows how to register.
|
|
19
|
+
*
|
|
20
|
+
* A host iterates this list; it does not switch on model names, which is what
|
|
21
|
+
* ARCH-02 and EXT-01 require of model-independent code.
|
|
22
|
+
*/
|
|
23
|
+
export const STANDARD_CONTRACTS = [sttTranscribe, langDetect];
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared result envelope every Tool Mode tool returns (DECL-03).
|
|
3
|
+
*
|
|
4
|
+
* The envelope is what makes a result self-describing: a caller can tell which
|
|
5
|
+
* contract and version produced it, which implementation actually ran, where it
|
|
6
|
+
* ran, and whether the host attached any warnings. The host stamps the
|
|
7
|
+
* authoritative provenance fields; an implementation cannot forge another
|
|
8
|
+
* package's identity.
|
|
9
|
+
*
|
|
10
|
+
* DECL-04 is the reason `warnings` exists but a universal `confidence` does not:
|
|
11
|
+
* a successful call means a valid result was produced, not that it is correct,
|
|
12
|
+
* and a fabricated confidence number would claim more than the model supplied.
|
|
13
|
+
*
|
|
14
|
+
* @module @try-works/tool-mode-contracts/meta
|
|
15
|
+
*/
|
|
16
|
+
/** Where an implementation executed, relative to the caller (SPEC §6). */
|
|
17
|
+
export type ExecutionLocation =
|
|
18
|
+
/** The harness host process itself. */
|
|
19
|
+
'host'
|
|
20
|
+
/** The user's own device. */
|
|
21
|
+
| 'device'
|
|
22
|
+
/** A user-controlled server on the local network. */
|
|
23
|
+
| 'team-server'
|
|
24
|
+
/** A remote service outside the user's control. */
|
|
25
|
+
| 'remote';
|
|
26
|
+
/** Provenance and status metadata shared by every tool result. */
|
|
27
|
+
export interface ToolMeta {
|
|
28
|
+
/** Contract identity, e.g. `stt.transcribe`. */
|
|
29
|
+
readonly contract: string;
|
|
30
|
+
/** Semantic version of the CONTRACT, independent of the host release. */
|
|
31
|
+
readonly contractVersion: string;
|
|
32
|
+
/** The implementation that actually executed, e.g. `voz-onnx`. */
|
|
33
|
+
readonly implementationId: string;
|
|
34
|
+
/** Owning extension package identity. */
|
|
35
|
+
readonly extension: string;
|
|
36
|
+
/** Extension version, stamped by the host. */
|
|
37
|
+
readonly extensionVersion: string;
|
|
38
|
+
/** Which activation generation produced this result (DECL-08). */
|
|
39
|
+
readonly activationGeneration: number;
|
|
40
|
+
/** Where the work happened. */
|
|
41
|
+
readonly executionLocation: ExecutionLocation;
|
|
42
|
+
/** Host-issued receipt identity, for correlation and audit. */
|
|
43
|
+
readonly receiptId: string;
|
|
44
|
+
/** Non-fatal conditions the caller should know about. */
|
|
45
|
+
readonly warnings: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
/** The JSON Schema for {@link ToolMeta}, used to validate every result. */
|
|
48
|
+
export declare const TOOL_META_SCHEMA: {
|
|
49
|
+
readonly type: "object";
|
|
50
|
+
readonly additionalProperties: false;
|
|
51
|
+
readonly required: readonly ["contract", "contractVersion", "implementationId", "extension", "extensionVersion", "activationGeneration", "executionLocation", "receiptId", "warnings"];
|
|
52
|
+
readonly properties: {
|
|
53
|
+
readonly contract: {
|
|
54
|
+
readonly type: "string";
|
|
55
|
+
readonly minLength: 1;
|
|
56
|
+
};
|
|
57
|
+
readonly contractVersion: {
|
|
58
|
+
readonly type: "string";
|
|
59
|
+
readonly pattern: "^\\d+\\.\\d+\\.\\d+$";
|
|
60
|
+
};
|
|
61
|
+
readonly implementationId: {
|
|
62
|
+
readonly type: "string";
|
|
63
|
+
readonly minLength: 1;
|
|
64
|
+
};
|
|
65
|
+
readonly extension: {
|
|
66
|
+
readonly type: "string";
|
|
67
|
+
readonly minLength: 1;
|
|
68
|
+
};
|
|
69
|
+
readonly extensionVersion: {
|
|
70
|
+
readonly type: "string";
|
|
71
|
+
readonly pattern: "^\\d+\\.\\d+\\.\\d+";
|
|
72
|
+
};
|
|
73
|
+
readonly activationGeneration: {
|
|
74
|
+
readonly type: "integer";
|
|
75
|
+
readonly minimum: 0;
|
|
76
|
+
};
|
|
77
|
+
readonly executionLocation: {
|
|
78
|
+
readonly type: "string";
|
|
79
|
+
readonly enum: readonly ["host", "device", "team-server", "remote"];
|
|
80
|
+
};
|
|
81
|
+
readonly receiptId: {
|
|
82
|
+
readonly type: "string";
|
|
83
|
+
readonly minLength: 1;
|
|
84
|
+
};
|
|
85
|
+
readonly warnings: {
|
|
86
|
+
readonly type: "array";
|
|
87
|
+
readonly items: {
|
|
88
|
+
readonly type: "string";
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Attach a `meta` property to a result schema.
|
|
95
|
+
*
|
|
96
|
+
* Contracts declare their own payload; the envelope is identical everywhere, so
|
|
97
|
+
* it is composed here rather than retyped in each contract — retyping it is how
|
|
98
|
+
* the shapes drift apart.
|
|
99
|
+
* @param payload - the contract's payload schema properties.
|
|
100
|
+
* @param requiredPayload - payload property names that are required.
|
|
101
|
+
* @returns a closed object schema containing the payload plus `meta`.
|
|
102
|
+
*/
|
|
103
|
+
export declare function withMeta(payload: Record<string, unknown>, requiredPayload?: readonly string[]): Record<string, unknown>;
|
|
104
|
+
//# sourceMappingURL=meta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../src/meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,0EAA0E;AAC1E,MAAM,MAAM,iBAAiB;AAC3B,uCAAuC;AACrC,MAAM;AACR,6BAA6B;GAC3B,QAAQ;AACV,qDAAqD;GACnD,aAAa;AACf,mDAAmD;GACjD,QAAQ,CAAA;AAEZ,kEAAkE;AAClE,MAAM,WAAW,QAAQ;IACvB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,yEAAyE;IACzE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,kEAAkE;IAClE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,yCAAyC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,kEAAkE;IAClE,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAA;IACrC,+BAA+B;IAC/B,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAA;IAC7C,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAED,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmBnB,CAAA;AAEV;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,eAAe,GAAE,SAAS,MAAM,EAAO,GACtC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared result envelope every Tool Mode tool returns (DECL-03).
|
|
3
|
+
*
|
|
4
|
+
* The envelope is what makes a result self-describing: a caller can tell which
|
|
5
|
+
* contract and version produced it, which implementation actually ran, where it
|
|
6
|
+
* ran, and whether the host attached any warnings. The host stamps the
|
|
7
|
+
* authoritative provenance fields; an implementation cannot forge another
|
|
8
|
+
* package's identity.
|
|
9
|
+
*
|
|
10
|
+
* DECL-04 is the reason `warnings` exists but a universal `confidence` does not:
|
|
11
|
+
* a successful call means a valid result was produced, not that it is correct,
|
|
12
|
+
* and a fabricated confidence number would claim more than the model supplied.
|
|
13
|
+
*
|
|
14
|
+
* @module @try-works/tool-mode-contracts/meta
|
|
15
|
+
*/
|
|
16
|
+
/** The JSON Schema for {@link ToolMeta}, used to validate every result. */
|
|
17
|
+
export const TOOL_META_SCHEMA = {
|
|
18
|
+
type: 'object',
|
|
19
|
+
additionalProperties: false,
|
|
20
|
+
required: [
|
|
21
|
+
'contract', 'contractVersion', 'implementationId', 'extension',
|
|
22
|
+
'extensionVersion', 'activationGeneration', 'executionLocation',
|
|
23
|
+
'receiptId', 'warnings',
|
|
24
|
+
],
|
|
25
|
+
properties: {
|
|
26
|
+
contract: { type: 'string', minLength: 1 },
|
|
27
|
+
contractVersion: { type: 'string', pattern: '^\\d+\\.\\d+\\.\\d+$' },
|
|
28
|
+
implementationId: { type: 'string', minLength: 1 },
|
|
29
|
+
extension: { type: 'string', minLength: 1 },
|
|
30
|
+
extensionVersion: { type: 'string', pattern: '^\\d+\\.\\d+\\.\\d+' },
|
|
31
|
+
activationGeneration: { type: 'integer', minimum: 0 },
|
|
32
|
+
executionLocation: { type: 'string', enum: ['host', 'device', 'team-server', 'remote'] },
|
|
33
|
+
receiptId: { type: 'string', minLength: 1 },
|
|
34
|
+
warnings: { type: 'array', items: { type: 'string' } },
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Attach a `meta` property to a result schema.
|
|
39
|
+
*
|
|
40
|
+
* Contracts declare their own payload; the envelope is identical everywhere, so
|
|
41
|
+
* it is composed here rather than retyped in each contract — retyping it is how
|
|
42
|
+
* the shapes drift apart.
|
|
43
|
+
* @param payload - the contract's payload schema properties.
|
|
44
|
+
* @param requiredPayload - payload property names that are required.
|
|
45
|
+
* @returns a closed object schema containing the payload plus `meta`.
|
|
46
|
+
*/
|
|
47
|
+
export function withMeta(payload, requiredPayload = []) {
|
|
48
|
+
return {
|
|
49
|
+
type: 'object',
|
|
50
|
+
additionalProperties: false,
|
|
51
|
+
required: [...requiredPayload, 'meta'],
|
|
52
|
+
properties: { ...payload, meta: TOOL_META_SCHEMA },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=meta.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract definitions and their runtime validation.
|
|
3
|
+
*
|
|
4
|
+
* A contract is the tool's public identity: stable name, version, input and
|
|
5
|
+
* output schema, semantics, limits and policy metadata (DECL-01). It is
|
|
6
|
+
* deliberately model-free — implementations bind to it, it does not know about
|
|
7
|
+
* them — which is what lets two models serve one `stt_transcribe` instead of
|
|
8
|
+
* producing a duplicate tool each (BASE-02).
|
|
9
|
+
*
|
|
10
|
+
* Milestone A is explicit that "a copied schema with no runtime validation is not
|
|
11
|
+
* completion", so this module validates both directions: the contract DEFINITION
|
|
12
|
+
* is checked when registered, and every RESULT is checked against the declared
|
|
13
|
+
* output schema before it reaches a caller.
|
|
14
|
+
*
|
|
15
|
+
* @module @try-works/tool-mode-contracts/registry
|
|
16
|
+
*/
|
|
17
|
+
import { TOOL_META_SCHEMA } from './meta.ts';
|
|
18
|
+
/** One tool contract: the authored-once public identity (DECL-01). */
|
|
19
|
+
export interface ToolContract {
|
|
20
|
+
/** Contract identity, e.g. `stt.transcribe`. Never a model name. */
|
|
21
|
+
readonly contract: string;
|
|
22
|
+
/** Semantic version, independent of any host release number. */
|
|
23
|
+
readonly version: string;
|
|
24
|
+
/** The tool name callers see, e.g. `stt_transcribe`. */
|
|
25
|
+
readonly toolName: string;
|
|
26
|
+
/** Concise description, used verbatim for DSH registration. */
|
|
27
|
+
readonly description: string;
|
|
28
|
+
/** Input schema as JSON Schema. The object root is closed. */
|
|
29
|
+
readonly inputSchema: Record<string, unknown>;
|
|
30
|
+
/** Output schema as JSON Schema, including the `meta` envelope. */
|
|
31
|
+
readonly outputSchema: Record<string, unknown>;
|
|
32
|
+
/** Declared semantics a caller must be able to rely on. */
|
|
33
|
+
readonly semantics: Readonly<Record<string, string | number | boolean>>;
|
|
34
|
+
/** Admitted resource limits, in the units the schema declares. */
|
|
35
|
+
readonly limits: Readonly<Record<string, number>>;
|
|
36
|
+
/** Unsupported cases the caller should be told about. */
|
|
37
|
+
readonly unsupported: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
/** A contract that failed validation, with every reason. */
|
|
40
|
+
export declare class ContractError extends Error {
|
|
41
|
+
readonly name = "ContractError";
|
|
42
|
+
/** The individual violations, in declaration order. */
|
|
43
|
+
readonly violations: readonly string[];
|
|
44
|
+
constructor(subject: string, violations: readonly string[]);
|
|
45
|
+
}
|
|
46
|
+
/** A result that did not satisfy its contract's output schema. */
|
|
47
|
+
export declare class ContractViolationError extends Error {
|
|
48
|
+
readonly name = "ContractViolationError";
|
|
49
|
+
readonly violations: readonly string[];
|
|
50
|
+
constructor(contract: string, violations: readonly string[]);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Validate a contract definition.
|
|
54
|
+
*
|
|
55
|
+
* This is deliberately strict about the identity fields: a contract whose
|
|
56
|
+
* `toolName` is not a legal tool name, or whose version is not semver, would
|
|
57
|
+
* register a tool that callers cannot address consistently.
|
|
58
|
+
* @param contract - the definition to check.
|
|
59
|
+
* @throws ContractError listing every violation.
|
|
60
|
+
*/
|
|
61
|
+
export declare function assertValidContract(contract: ToolContract): void;
|
|
62
|
+
/**
|
|
63
|
+
* Validate caller arguments against the contract's input schema.
|
|
64
|
+
* @param contract - the contract to check against.
|
|
65
|
+
* @param args - the candidate arguments.
|
|
66
|
+
* @throws ContractViolationError listing every violation.
|
|
67
|
+
*/
|
|
68
|
+
export declare function assertValidInput(contract: ToolContract, args: unknown): void;
|
|
69
|
+
/**
|
|
70
|
+
* Validate a result against the contract's output schema.
|
|
71
|
+
*
|
|
72
|
+
* This runs before the value reaches a caller, so a violation surfaces as a
|
|
73
|
+
* tool failure rather than as malformed data downstream (AT-11).
|
|
74
|
+
* @param contract - the contract to check against.
|
|
75
|
+
* @param value - the candidate result.
|
|
76
|
+
* @throws ContractViolationError listing every violation.
|
|
77
|
+
*/
|
|
78
|
+
export declare function assertValidOutput(contract: ToolContract, value: unknown): void;
|
|
79
|
+
/** The meta envelope's own schema, exported for contract authors. */
|
|
80
|
+
export { TOOL_META_SCHEMA };
|
|
81
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAE5C,sEAAsE;AACtE,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7C,mEAAmE;IACnE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9C,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,CAAA;IACvE,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACjD,yDAAyD;IACzD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;CACxC;AAED,4DAA4D;AAC5D,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAkB,IAAI,mBAAkB;IACxC,uDAAuD;IACvD,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;gBAC1B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE;CAI3D;AAED,kEAAkE;AAClE,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,SAAkB,IAAI,4BAA2B;IACjD,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;gBAC1B,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE;CAI5D;AA8BD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAmDhE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAM5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAM9E;AAED,qEAAqE;AACrE,OAAO,EAAE,gBAAgB,EAAE,CAAA"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract definitions and their runtime validation.
|
|
3
|
+
*
|
|
4
|
+
* A contract is the tool's public identity: stable name, version, input and
|
|
5
|
+
* output schema, semantics, limits and policy metadata (DECL-01). It is
|
|
6
|
+
* deliberately model-free — implementations bind to it, it does not know about
|
|
7
|
+
* them — which is what lets two models serve one `stt_transcribe` instead of
|
|
8
|
+
* producing a duplicate tool each (BASE-02).
|
|
9
|
+
*
|
|
10
|
+
* Milestone A is explicit that "a copied schema with no runtime validation is not
|
|
11
|
+
* completion", so this module validates both directions: the contract DEFINITION
|
|
12
|
+
* is checked when registered, and every RESULT is checked against the declared
|
|
13
|
+
* output schema before it reaches a caller.
|
|
14
|
+
*
|
|
15
|
+
* @module @try-works/tool-mode-contracts/registry
|
|
16
|
+
*/
|
|
17
|
+
import { Ajv } from 'ajv';
|
|
18
|
+
import { TOOL_META_SCHEMA } from "./meta.js";
|
|
19
|
+
/** A contract that failed validation, with every reason. */
|
|
20
|
+
export class ContractError extends Error {
|
|
21
|
+
name = 'ContractError';
|
|
22
|
+
/** The individual violations, in declaration order. */
|
|
23
|
+
violations;
|
|
24
|
+
constructor(subject, violations) {
|
|
25
|
+
super(`${subject} is not a valid contract: ${violations.join('; ')}`);
|
|
26
|
+
this.violations = violations;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** A result that did not satisfy its contract's output schema. */
|
|
30
|
+
export class ContractViolationError extends Error {
|
|
31
|
+
name = 'ContractViolationError';
|
|
32
|
+
violations;
|
|
33
|
+
constructor(contract, violations) {
|
|
34
|
+
super(`result violates the ${contract} contract: ${violations.join('; ')}`);
|
|
35
|
+
this.violations = violations;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The validator. `strict: false` because contract schemas use JSON Schema
|
|
40
|
+
* keywords that Ajv's strict mode rejects when they are merely annotated, and
|
|
41
|
+
* `allErrors` so a violation report lists every problem rather than the first.
|
|
42
|
+
*/
|
|
43
|
+
function createAjv() {
|
|
44
|
+
return new Ajv({ allErrors: true, strict: false, allowUnionTypes: true });
|
|
45
|
+
}
|
|
46
|
+
const ajv = createAjv();
|
|
47
|
+
const outputValidators = new Map();
|
|
48
|
+
const inputValidators = new Map();
|
|
49
|
+
/** Render Ajv errors as stable, deduplicated strings. */
|
|
50
|
+
function renderErrors(errors) {
|
|
51
|
+
const seen = new Set();
|
|
52
|
+
const out = [];
|
|
53
|
+
for (const error of errors ?? []) {
|
|
54
|
+
const path = error.instancePath === '' ? '(root)' : error.instancePath;
|
|
55
|
+
const line = `${path} ${error.message ?? 'is invalid'}`;
|
|
56
|
+
if (!seen.has(line)) {
|
|
57
|
+
seen.add(line);
|
|
58
|
+
out.push(line);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validate a contract definition.
|
|
65
|
+
*
|
|
66
|
+
* This is deliberately strict about the identity fields: a contract whose
|
|
67
|
+
* `toolName` is not a legal tool name, or whose version is not semver, would
|
|
68
|
+
* register a tool that callers cannot address consistently.
|
|
69
|
+
* @param contract - the definition to check.
|
|
70
|
+
* @throws ContractError listing every violation.
|
|
71
|
+
*/
|
|
72
|
+
export function assertValidContract(contract) {
|
|
73
|
+
const violations = [];
|
|
74
|
+
if (!/^[a-z][a-z0-9_]*$/.test(contract.toolName)) {
|
|
75
|
+
violations.push(`toolName ${JSON.stringify(contract.toolName)} must be lower snake_case`);
|
|
76
|
+
}
|
|
77
|
+
if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/.test(contract.contract)) {
|
|
78
|
+
violations.push(`contract ${JSON.stringify(contract.contract)} must be a dotted lower-case identity `
|
|
79
|
+
+ '(for example "stt.transcribe" or "text.language_detect")');
|
|
80
|
+
}
|
|
81
|
+
if (!/^\d+\.\d+\.\d+$/.test(contract.version)) {
|
|
82
|
+
violations.push(`version ${JSON.stringify(contract.version)} must be semantic (x.y.z)`);
|
|
83
|
+
}
|
|
84
|
+
if (contract.description.trim().length < 10) {
|
|
85
|
+
violations.push('description must be a real sentence, not a placeholder');
|
|
86
|
+
}
|
|
87
|
+
const input = contract.inputSchema;
|
|
88
|
+
if (input.type !== 'object') {
|
|
89
|
+
violations.push('inputSchema root must be an object (clients require an object argument)');
|
|
90
|
+
}
|
|
91
|
+
const output = contract.outputSchema;
|
|
92
|
+
if (output.type !== 'object') {
|
|
93
|
+
violations.push('outputSchema root must be an object for broad client compatibility (DECL-02)');
|
|
94
|
+
}
|
|
95
|
+
else if (output.properties?.meta === undefined) {
|
|
96
|
+
violations.push('outputSchema must include the shared `meta` envelope (DECL-03)');
|
|
97
|
+
}
|
|
98
|
+
// DECL-02: an unconstrained open object is not an adequate leaf-tool contract.
|
|
99
|
+
if (output.additionalProperties !== false) {
|
|
100
|
+
violations.push('outputSchema must be closed (additionalProperties: false), not a generic open object (DECL-02)');
|
|
101
|
+
}
|
|
102
|
+
if (contract.unsupported.length === 0) {
|
|
103
|
+
violations.push('unsupported must name at least one case; an empty list claims universal support');
|
|
104
|
+
}
|
|
105
|
+
if (violations.length > 0)
|
|
106
|
+
throw new ContractError(contract.contract, violations);
|
|
107
|
+
// Schemas must be compilable, or the contract is unenforceable.
|
|
108
|
+
try {
|
|
109
|
+
inputValidators.set(contract.contract, ajv.compile(contract.inputSchema));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
throw new ContractError(contract.contract, [`inputSchema does not compile: ${message(error)}`]);
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
outputValidators.set(contract.contract, ajv.compile(contract.outputSchema));
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
throw new ContractError(contract.contract, [`outputSchema does not compile: ${message(error)}`]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Validate caller arguments against the contract's input schema.
|
|
123
|
+
* @param contract - the contract to check against.
|
|
124
|
+
* @param args - the candidate arguments.
|
|
125
|
+
* @throws ContractViolationError listing every violation.
|
|
126
|
+
*/
|
|
127
|
+
export function assertValidInput(contract, args) {
|
|
128
|
+
const validate = inputValidators.get(contract.contract);
|
|
129
|
+
if (validate === undefined) {
|
|
130
|
+
throw new ContractError(contract.contract, ['input was validated before the contract was registered']);
|
|
131
|
+
}
|
|
132
|
+
if (!validate(args))
|
|
133
|
+
throw new ContractViolationError(contract.contract, renderErrors(validate.errors));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Validate a result against the contract's output schema.
|
|
137
|
+
*
|
|
138
|
+
* This runs before the value reaches a caller, so a violation surfaces as a
|
|
139
|
+
* tool failure rather than as malformed data downstream (AT-11).
|
|
140
|
+
* @param contract - the contract to check against.
|
|
141
|
+
* @param value - the candidate result.
|
|
142
|
+
* @throws ContractViolationError listing every violation.
|
|
143
|
+
*/
|
|
144
|
+
export function assertValidOutput(contract, value) {
|
|
145
|
+
const validate = outputValidators.get(contract.contract);
|
|
146
|
+
if (validate === undefined) {
|
|
147
|
+
throw new ContractError(contract.contract, ['result was validated before the contract was registered']);
|
|
148
|
+
}
|
|
149
|
+
if (!validate(value))
|
|
150
|
+
throw new ContractViolationError(contract.contract, renderErrors(validate.errors));
|
|
151
|
+
}
|
|
152
|
+
/** The meta envelope's own schema, exported for contract authors. */
|
|
153
|
+
export { TOOL_META_SCHEMA };
|
|
154
|
+
/** Best-effort message from an arbitrary thrown value. */
|
|
155
|
+
function message(error) {
|
|
156
|
+
try {
|
|
157
|
+
return error instanceof Error ? error.message : String(error);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return '<unrenderable>';
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@try-works/tool-mode-contracts",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Model-free standard tool contracts for Tool Mode: identities, schemas, semantics and runtime validation",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/try-works/dsh-model-as-tool.git",
|
|
10
|
+
"directory": "packages/tool-mode-contracts"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "lib/index.js",
|
|
14
|
+
"types": "lib/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": { "types": "./lib/index.d.ts", "default": "./lib/index.js" },
|
|
17
|
+
"./src/*": "./src/*",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"files": ["lib/**/*.js", "lib/**/*.d.ts", "lib/**/*.d.ts.map", "README.md"],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"ajv": "^8.20.0"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.build.json",
|
|
26
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
27
|
+
"test": "tsx --test tests/*.test.ts",
|
|
28
|
+
"prepack": "tsc -p tsconfig.build.json",
|
|
29
|
+
"prepare": "tsc -p tsconfig.build.json"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^24.0.0",
|
|
33
|
+
"typescript": "^5.9.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@try-works/dsh-model-as-tool",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Install Desert Ant Labs on-device models as DSH tools and skills, with per-platform capability gating",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -76,17 +76,22 @@
|
|
|
76
76
|
"build": "tsc -p tsconfig.build.json",
|
|
77
77
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
78
78
|
"test": "tsx --test tests/*.test.ts",
|
|
79
|
-
"prepack": "
|
|
80
|
-
"prepare": "tsc -p tsconfig.build.json"
|
|
79
|
+
"prepack": "node scripts/stage-bundle.mjs && npm run build",
|
|
80
|
+
"prepare": "tsc -p tsconfig.build.json",
|
|
81
|
+
"postpack": "node scripts/stage-bundle.mjs --clean"
|
|
81
82
|
},
|
|
82
83
|
"devDependencies": {
|
|
83
84
|
"@types/node": "^24.0.0",
|
|
84
85
|
"typescript": "^5.9.0"
|
|
85
86
|
},
|
|
86
87
|
"dependencies": {
|
|
87
|
-
"@try-works/tool-mode-contracts": "^0.1.0"
|
|
88
|
+
"@try-works/tool-mode-contracts": "^0.1.0",
|
|
89
|
+
"ajv": "^8.20.0"
|
|
88
90
|
},
|
|
89
91
|
"bin": {
|
|
90
92
|
"dsh-tool-mode-mcp-stdio": "./bin/dsh-tool-mode-mcp-stdio.mjs"
|
|
91
|
-
}
|
|
93
|
+
},
|
|
94
|
+
"bundledDependencies": [
|
|
95
|
+
"@try-works/tool-mode-contracts"
|
|
96
|
+
]
|
|
92
97
|
}
|