@jini-ai/daemon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/agent-executor.d.ts +458 -0
- package/dist/agent-executor.d.ts.map +1 -0
- package/dist/agent-executor.js +1277 -0
- package/dist/agent-executor.js.map +1 -0
- package/dist/artifacts/index.d.ts +14 -0
- package/dist/artifacts/index.d.ts.map +1 -0
- package/dist/artifacts/index.js +14 -0
- package/dist/artifacts/index.js.map +1 -0
- package/dist/artifacts/manifest.d.ts +90 -0
- package/dist/artifacts/manifest.d.ts.map +1 -0
- package/dist/artifacts/manifest.js +229 -0
- package/dist/artifacts/manifest.js.map +1 -0
- package/dist/artifacts/publication-guard.d.ts +28 -0
- package/dist/artifacts/publication-guard.d.ts.map +1 -0
- package/dist/artifacts/publication-guard.js +77 -0
- package/dist/artifacts/publication-guard.js.map +1 -0
- package/dist/artifacts/runtime-compat.d.ts +37 -0
- package/dist/artifacts/runtime-compat.d.ts.map +1 -0
- package/dist/artifacts/runtime-compat.js +33 -0
- package/dist/artifacts/runtime-compat.js.map +1 -0
- package/dist/artifacts/store.d.ts +85 -0
- package/dist/artifacts/store.d.ts.map +1 -0
- package/dist/artifacts/store.js +92 -0
- package/dist/artifacts/store.js.map +1 -0
- package/dist/artifacts/stub-guard.d.ts +73 -0
- package/dist/artifacts/stub-guard.d.ts.map +1 -0
- package/dist/artifacts/stub-guard.js +198 -0
- package/dist/artifacts/stub-guard.js.map +1 -0
- package/dist/artifacts/text-suppression.d.ts +68 -0
- package/dist/artifacts/text-suppression.d.ts.map +1 -0
- package/dist/artifacts/text-suppression.js +184 -0
- package/dist/artifacts/text-suppression.js.map +1 -0
- package/dist/close-status.d.ts +86 -0
- package/dist/close-status.d.ts.map +1 -0
- package/dist/close-status.js +91 -0
- package/dist/close-status.js.map +1 -0
- package/dist/continuation/continuation-transport.d.ts +41 -0
- package/dist/continuation/continuation-transport.d.ts.map +1 -0
- package/dist/continuation/continuation-transport.js +18 -0
- package/dist/continuation/continuation-transport.js.map +1 -0
- package/dist/continuation/index.d.ts +4 -0
- package/dist/continuation/index.d.ts.map +1 -0
- package/dist/continuation/index.js +4 -0
- package/dist/continuation/index.js.map +1 -0
- package/dist/continuation/journal.d.ts +37 -0
- package/dist/continuation/journal.d.ts.map +1 -0
- package/dist/continuation/journal.js +23 -0
- package/dist/continuation/journal.js.map +1 -0
- package/dist/continuation/run-start-handler.d.ts +75 -0
- package/dist/continuation/run-start-handler.d.ts.map +1 -0
- package/dist/continuation/run-start-handler.js +32 -0
- package/dist/continuation/run-start-handler.js.map +1 -0
- package/dist/delegated-tool-bridge.d.ts +53 -0
- package/dist/delegated-tool-bridge.d.ts.map +1 -0
- package/dist/delegated-tool-bridge.js +92 -0
- package/dist/delegated-tool-bridge.js.map +1 -0
- package/dist/event-log.d.ts +134 -0
- package/dist/event-log.d.ts.map +1 -0
- package/dist/event-log.js +108 -0
- package/dist/event-log.js.map +1 -0
- package/dist/frontend-capability-tools.d.ts +102 -0
- package/dist/frontend-capability-tools.d.ts.map +1 -0
- package/dist/frontend-capability-tools.js +68 -0
- package/dist/frontend-capability-tools.js.map +1 -0
- package/dist/frontend-session-registry.d.ts +127 -0
- package/dist/frontend-session-registry.d.ts.map +1 -0
- package/dist/frontend-session-registry.js +180 -0
- package/dist/frontend-session-registry.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/legacy-data-migration.d.ts +104 -0
- package/dist/legacy-data-migration.d.ts.map +1 -0
- package/dist/legacy-data-migration.js +239 -0
- package/dist/legacy-data-migration.js.map +1 -0
- package/dist/routines/index.d.ts +12 -0
- package/dist/routines/index.d.ts.map +1 -0
- package/dist/routines/index.js +12 -0
- package/dist/routines/index.js.map +1 -0
- package/dist/routines/routine-store.d.ts +77 -0
- package/dist/routines/routine-store.d.ts.map +1 -0
- package/dist/routines/routine-store.js +162 -0
- package/dist/routines/routine-store.js.map +1 -0
- package/dist/routines/schedule.d.ts +72 -0
- package/dist/routines/schedule.d.ts.map +1 -0
- package/dist/routines/schedule.js +263 -0
- package/dist/routines/schedule.js.map +1 -0
- package/dist/routines/scheduler.d.ts +34 -0
- package/dist/routines/scheduler.d.ts.map +1 -0
- package/dist/routines/scheduler.js +330 -0
- package/dist/routines/scheduler.js.map +1 -0
- package/dist/routines/types.d.ts +131 -0
- package/dist/routines/types.d.ts.map +1 -0
- package/dist/routines/types.js +9 -0
- package/dist/routines/types.js.map +1 -0
- package/dist/run/core/failure-taxonomy.d.ts +27 -0
- package/dist/run/core/failure-taxonomy.d.ts.map +1 -0
- package/dist/run/core/failure-taxonomy.js +16 -0
- package/dist/run/core/failure-taxonomy.js.map +1 -0
- package/dist/run/core/index.d.ts +5 -0
- package/dist/run/core/index.d.ts.map +1 -0
- package/dist/run/core/index.js +5 -0
- package/dist/run/core/index.js.map +1 -0
- package/dist/run/core/result.d.ts +25 -0
- package/dist/run/core/result.d.ts.map +1 -0
- package/dist/run/core/result.js +52 -0
- package/dist/run/core/result.js.map +1 -0
- package/dist/run/core/retry.d.ts +106 -0
- package/dist/run/core/retry.d.ts.map +1 -0
- package/dist/run/core/retry.js +210 -0
- package/dist/run/core/retry.js.map +1 -0
- package/dist/run/diagnostics/diagnostics.d.ts +86 -0
- package/dist/run/diagnostics/diagnostics.d.ts.map +1 -0
- package/dist/run/diagnostics/diagnostics.js +211 -0
- package/dist/run/diagnostics/diagnostics.js.map +1 -0
- package/dist/run/diagnostics/index.d.ts +3 -0
- package/dist/run/diagnostics/index.d.ts.map +1 -0
- package/dist/run/diagnostics/index.js +3 -0
- package/dist/run/diagnostics/index.js.map +1 -0
- package/dist/run/index.d.ts +16 -0
- package/dist/run/index.d.ts.map +1 -0
- package/dist/run/index.js +16 -0
- package/dist/run/index.js.map +1 -0
- package/dist/run-lifecycle.d.ts +118 -0
- package/dist/run-lifecycle.d.ts.map +1 -0
- package/dist/run-lifecycle.js +451 -0
- package/dist/run-lifecycle.js.map +1 -0
- package/dist/terminal-session.d.ts +139 -0
- package/dist/terminal-session.d.ts.map +1 -0
- package/dist/terminal-session.js +316 -0
- package/dist/terminal-session.js.map +1 -0
- package/dist/tokens.d.ts +9 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +19 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tool-executor.d.ts +97 -0
- package/dist/tool-executor.d.ts.map +1 -0
- package/dist/tool-executor.js +198 -0
- package/dist/tool-executor.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module artifacts/runtime-compat
|
|
3
|
+
*
|
|
4
|
+
* The runtime-compat injection seam: a hook that gets a chance to rewrite an
|
|
5
|
+
* artifact's body before it's served, for CDN/library-loading quirks a host
|
|
6
|
+
* knows about but the engine can't. Ported from OD's
|
|
7
|
+
* `apps/daemon/src/artifacts/runtime-compat.ts`.
|
|
8
|
+
*
|
|
9
|
+
* De-branded: the origin, `normalizeArtifactRuntimeImports`, is entirely a
|
|
10
|
+
* fix for one specific third-party CDN-bundle bug (the vanilla `dist/
|
|
11
|
+
* motion.js` UMD script tag lacking React hooks, rewritten to `dist/
|
|
12
|
+
* framer-motion.js`) that OD's own system prompt steers models toward
|
|
13
|
+
* hitting. That rewrite rule is product/library-specific knowledge, not a
|
|
14
|
+
* generic engine mechanism — per the task brief ("keep OD's specific logic
|
|
15
|
+
* as adapter"), it is not ported. What *is* generic and kept is the
|
|
16
|
+
* seam itself: a host may need to rewrite an artifact body for any number
|
|
17
|
+
* of environment-specific reasons (a different CDN quirk, a different
|
|
18
|
+
* runtime library entirely) before it reaches a renderer, so the engine
|
|
19
|
+
* defines the hook type and a safe default (identity) rather than hardcode
|
|
20
|
+
* any one library's fix.
|
|
21
|
+
*/
|
|
22
|
+
/** Applies no rewrite — returns `body` unchanged. Safe default until a host supplies its own normalizer(s). */
|
|
23
|
+
export const noopRuntimeCompatNormalizer = (_name, body) => body;
|
|
24
|
+
/**
|
|
25
|
+
* Composes several normalizers into one, applying each in order — the
|
|
26
|
+
* output of one becomes the input to the next. Lets a host layer multiple
|
|
27
|
+
* independent CDN/library fixes without each needing to know about the
|
|
28
|
+
* others.
|
|
29
|
+
*/
|
|
30
|
+
export function composeRuntimeCompatNormalizers(normalizers) {
|
|
31
|
+
return (name, body) => normalizers.reduce((current, normalize) => normalize(name, current), body);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=runtime-compat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-compat.js","sourceRoot":"","sources":["../../src/artifacts/runtime-compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,+GAA+G;AAC/G,MAAM,CAAC,MAAM,2BAA2B,GAA4B,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;AAE1F;;;;;GAKG;AACH,MAAM,UAAU,+BAA+B,CAC7C,WAA+C;IAE/C,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;AACpG,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module artifacts/store
|
|
3
|
+
*
|
|
4
|
+
* `ArtifactStore` — the generic artifact-store kernel port. Ported from OD's
|
|
5
|
+
* `apps/daemon/src/artifacts/create.ts` create/manifest-resolution flow;
|
|
6
|
+
* "artifacts feel like a kernel-adjacent concept tied to runs producing
|
|
7
|
+
* output" per this task's brief — homed in `@jini/daemon` alongside
|
|
8
|
+
* `EventLog` (same async-port + in-memory-reference-implementation shape as
|
|
9
|
+
* `event-log.ts`), not `@jini/core` (which owns pure registries/DI, not
|
|
10
|
+
* stateful storage).
|
|
11
|
+
*
|
|
12
|
+
* De-branded: the origin's `createProjectArtifactFile` took OD's own
|
|
13
|
+
* product-shaped workspace/file-tree writer (a function keyed on OD's own
|
|
14
|
+
* workspace-root and workspace-id nouns) as its injected dependency, and a
|
|
15
|
+
* companion `postCreateArtifactRequest` built a request body for OD's own
|
|
16
|
+
* per-workspace HTTP file-upload route. Neither is a generic engine concern
|
|
17
|
+
* (a workspace model and an HTTP route shape are product surfaces), so this
|
|
18
|
+
* port defines `ArtifactStore.create/get/list` directly against an
|
|
19
|
+
* in-memory reference map — the origin's writer/request-builder pair were
|
|
20
|
+
* the composition points a real OD adapter would wire against this port
|
|
21
|
+
* from, not something to port verbatim. OD's HTML-prototype file-kind
|
|
22
|
+
* inference (`inferLegacyManifest`) stays adapter-owned — see
|
|
23
|
+
* `manifest.ts`'s `ManifestInferrer` seam.
|
|
24
|
+
*/
|
|
25
|
+
import { type ArtifactManifest, type ArtifactManifestTaxonomy, type ManifestInferrer } from './manifest.js';
|
|
26
|
+
export declare class ArtifactManifestRequiredError extends Error {
|
|
27
|
+
readonly code: "ARTIFACT_MANIFEST_REQUIRED";
|
|
28
|
+
constructor(name: string);
|
|
29
|
+
}
|
|
30
|
+
export declare class ArtifactManifestInvalidError extends Error {
|
|
31
|
+
readonly code: "ARTIFACT_MANIFEST_INVALID";
|
|
32
|
+
constructor(message: string);
|
|
33
|
+
}
|
|
34
|
+
export interface CreateArtifactInput {
|
|
35
|
+
readonly name: string;
|
|
36
|
+
readonly content: string;
|
|
37
|
+
readonly encoding?: 'utf8' | 'base64';
|
|
38
|
+
/** Explicit manifest fields, or `undefined` to fall back to the store's {@link ManifestInferrer}. */
|
|
39
|
+
readonly artifactManifest?: unknown;
|
|
40
|
+
}
|
|
41
|
+
export interface ArtifactRecord {
|
|
42
|
+
readonly name: string;
|
|
43
|
+
readonly content: Buffer;
|
|
44
|
+
readonly manifest: ArtifactManifest;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A replayable, ordered artifact-manifest resolver: given a create request,
|
|
48
|
+
* either the caller-supplied manifest (validated against `taxonomy`) or the
|
|
49
|
+
* result of `inferManifest(name)` (also validated) becomes the record's
|
|
50
|
+
* manifest. Throws {@link ArtifactManifestRequiredError} when neither
|
|
51
|
+
* source produces one, {@link ArtifactManifestInvalidError} when the
|
|
52
|
+
* resolved manifest fails validation.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveArtifactManifest(input: CreateArtifactInput, taxonomy: ArtifactManifestTaxonomy, inferManifest: ManifestInferrer): ArtifactManifest;
|
|
55
|
+
/**
|
|
56
|
+
* A store of named artifacts with validated manifests. Kernel port —
|
|
57
|
+
* `@jini/daemon` ships `createInMemoryArtifactStore` as the reference
|
|
58
|
+
* implementation; a durable adapter (`@jini/sqlite`) can implement the same
|
|
59
|
+
* interface against real persistence.
|
|
60
|
+
*/
|
|
61
|
+
export interface ArtifactStore {
|
|
62
|
+
/**
|
|
63
|
+
* Creates or overwrites the artifact named `input.name`, resolving its
|
|
64
|
+
* manifest per {@link resolveArtifactManifest}.
|
|
65
|
+
* @throws {ArtifactManifestRequiredError} No manifest supplied and the inferrer produced none.
|
|
66
|
+
* @throws {ArtifactManifestInvalidError} The resolved manifest failed taxonomy validation.
|
|
67
|
+
*/
|
|
68
|
+
create(input: CreateArtifactInput): Promise<ArtifactRecord>;
|
|
69
|
+
/** Returns the named artifact, or `null` if it doesn't exist. */
|
|
70
|
+
get(name: string): Promise<ArtifactRecord | null>;
|
|
71
|
+
/** Returns every stored artifact, most-recently-updated first. */
|
|
72
|
+
list(): Promise<ArtifactRecord[]>;
|
|
73
|
+
}
|
|
74
|
+
export interface InMemoryArtifactStoreOptions {
|
|
75
|
+
readonly taxonomy?: ArtifactManifestTaxonomy;
|
|
76
|
+
readonly inferManifest?: ManifestInferrer;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Reference `ArtifactStore` implementation: an in-memory `Map` keyed by
|
|
80
|
+
* artifact name. No durable persistence — matching this task's scope (a
|
|
81
|
+
* real persistent adapter is a future storage task's concern, mirroring
|
|
82
|
+
* `event-log.ts`'s own in-memory-only reference implementation).
|
|
83
|
+
*/
|
|
84
|
+
export declare function createInMemoryArtifactStore(options?: InMemoryArtifactStoreOptions): ArtifactStore;
|
|
85
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/artifacts/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAEvB,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,QAAQ,CAAC,IAAI,EAAG,4BAA4B,CAAU;gBAE1C,IAAI,EAAE,MAAM;CAIzB;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,IAAI,EAAG,2BAA2B,CAAU;gBAEzC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACtC,qGAAqG;IACrG,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,EAAE,wBAAwB,EAClC,aAAa,EAAE,gBAAgB,GAC9B,gBAAgB,CAelB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5D,iEAAiE;IACjE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAClD,kEAAkE;IAClE,IAAI,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAC3C;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,GAAE,4BAAiC,GAAG,aAAa,CA0BrG"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module artifacts/store
|
|
3
|
+
*
|
|
4
|
+
* `ArtifactStore` — the generic artifact-store kernel port. Ported from OD's
|
|
5
|
+
* `apps/daemon/src/artifacts/create.ts` create/manifest-resolution flow;
|
|
6
|
+
* "artifacts feel like a kernel-adjacent concept tied to runs producing
|
|
7
|
+
* output" per this task's brief — homed in `@jini/daemon` alongside
|
|
8
|
+
* `EventLog` (same async-port + in-memory-reference-implementation shape as
|
|
9
|
+
* `event-log.ts`), not `@jini/core` (which owns pure registries/DI, not
|
|
10
|
+
* stateful storage).
|
|
11
|
+
*
|
|
12
|
+
* De-branded: the origin's `createProjectArtifactFile` took OD's own
|
|
13
|
+
* product-shaped workspace/file-tree writer (a function keyed on OD's own
|
|
14
|
+
* workspace-root and workspace-id nouns) as its injected dependency, and a
|
|
15
|
+
* companion `postCreateArtifactRequest` built a request body for OD's own
|
|
16
|
+
* per-workspace HTTP file-upload route. Neither is a generic engine concern
|
|
17
|
+
* (a workspace model and an HTTP route shape are product surfaces), so this
|
|
18
|
+
* port defines `ArtifactStore.create/get/list` directly against an
|
|
19
|
+
* in-memory reference map — the origin's writer/request-builder pair were
|
|
20
|
+
* the composition points a real OD adapter would wire against this port
|
|
21
|
+
* from, not something to port verbatim. OD's HTML-prototype file-kind
|
|
22
|
+
* inference (`inferLegacyManifest`) stays adapter-owned — see
|
|
23
|
+
* `manifest.ts`'s `ManifestInferrer` seam.
|
|
24
|
+
*/
|
|
25
|
+
import { emptyArtifactManifestTaxonomy, noopManifestInferrer, validateArtifactManifestInput, } from './manifest.js';
|
|
26
|
+
export class ArtifactManifestRequiredError extends Error {
|
|
27
|
+
code = 'ARTIFACT_MANIFEST_REQUIRED';
|
|
28
|
+
constructor(name) {
|
|
29
|
+
super(`artifactManifest is required for ${name}; no safe default manifest can be inferred`);
|
|
30
|
+
this.name = 'ArtifactManifestRequiredError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class ArtifactManifestInvalidError extends Error {
|
|
34
|
+
code = 'ARTIFACT_MANIFEST_INVALID';
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super(`invalid artifactManifest: ${message}`);
|
|
37
|
+
this.name = 'ArtifactManifestInvalidError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A replayable, ordered artifact-manifest resolver: given a create request,
|
|
42
|
+
* either the caller-supplied manifest (validated against `taxonomy`) or the
|
|
43
|
+
* result of `inferManifest(name)` (also validated) becomes the record's
|
|
44
|
+
* manifest. Throws {@link ArtifactManifestRequiredError} when neither
|
|
45
|
+
* source produces one, {@link ArtifactManifestInvalidError} when the
|
|
46
|
+
* resolved manifest fails validation.
|
|
47
|
+
*/
|
|
48
|
+
export function resolveArtifactManifest(input, taxonomy, inferManifest) {
|
|
49
|
+
const manifest = input.artifactManifest !== undefined && input.artifactManifest !== null
|
|
50
|
+
? input.artifactManifest
|
|
51
|
+
: inferManifest(input.name);
|
|
52
|
+
if (manifest) {
|
|
53
|
+
const validated = validateArtifactManifestInput(manifest, input.name, taxonomy);
|
|
54
|
+
if (!validated.ok) {
|
|
55
|
+
throw new ArtifactManifestInvalidError(validated.error);
|
|
56
|
+
}
|
|
57
|
+
// `manifest != null` above and `validateArtifactManifestInput` only
|
|
58
|
+
// returns `{ ok: true, value: null }` when its own `manifest` param is
|
|
59
|
+
// `== null` — so `validated.value` is non-null here.
|
|
60
|
+
return validated.value;
|
|
61
|
+
}
|
|
62
|
+
throw new ArtifactManifestRequiredError(input.name);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Reference `ArtifactStore` implementation: an in-memory `Map` keyed by
|
|
66
|
+
* artifact name. No durable persistence — matching this task's scope (a
|
|
67
|
+
* real persistent adapter is a future storage task's concern, mirroring
|
|
68
|
+
* `event-log.ts`'s own in-memory-only reference implementation).
|
|
69
|
+
*/
|
|
70
|
+
export function createInMemoryArtifactStore(options = {}) {
|
|
71
|
+
const taxonomy = options.taxonomy ?? emptyArtifactManifestTaxonomy;
|
|
72
|
+
const inferManifest = options.inferManifest ?? noopManifestInferrer;
|
|
73
|
+
const records = new Map();
|
|
74
|
+
return {
|
|
75
|
+
async create(input) {
|
|
76
|
+
const manifest = resolveArtifactManifest(input, taxonomy, inferManifest);
|
|
77
|
+
const content = input.encoding === 'base64'
|
|
78
|
+
? Buffer.from(input.content, 'base64')
|
|
79
|
+
: Buffer.from(input.content, 'utf8');
|
|
80
|
+
const record = { name: input.name, content, manifest };
|
|
81
|
+
records.set(input.name, record);
|
|
82
|
+
return record;
|
|
83
|
+
},
|
|
84
|
+
async get(name) {
|
|
85
|
+
return records.get(name) ?? null;
|
|
86
|
+
},
|
|
87
|
+
async list() {
|
|
88
|
+
return Array.from(records.values()).sort((a, b) => b.manifest.updatedAt.localeCompare(a.manifest.updatedAt));
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/artifacts/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,6BAA6B,GAI9B,MAAM,eAAe,CAAC;AAEvB,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IAC7C,IAAI,GAAG,4BAAqC,CAAC;IAEtD,YAAY,IAAY;QACtB,KAAK,CAAC,oCAAoC,IAAI,4CAA4C,CAAC,CAAC;QAC5F,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;IAC9C,CAAC;CACF;AAED,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IAC5C,IAAI,GAAG,2BAAoC,CAAC;IAErD,YAAY,OAAe;QACzB,KAAK,CAAC,6BAA6B,OAAO,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC7C,CAAC;CACF;AAgBD;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAA0B,EAC1B,QAAkC,EAClC,aAA+B;IAE/B,MAAM,QAAQ,GAAG,KAAK,CAAC,gBAAgB,KAAK,SAAS,IAAI,KAAK,CAAC,gBAAgB,KAAK,IAAI;QACtF,CAAC,CAAC,KAAK,CAAC,gBAAgB;QACxB,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,SAAS,GAAG,6BAA6B,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAChF,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;YAClB,MAAM,IAAI,4BAA4B,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1D,CAAC;QACD,oEAAoE;QACpE,uEAAuE;QACvE,qDAAqD;QACrD,OAAO,SAAS,CAAC,KAAM,CAAC;IAC1B,CAAC;IACD,MAAM,IAAI,6BAA6B,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACtD,CAAC;AA2BD;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CAAC,UAAwC,EAAE;IACpF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,6BAA6B,CAAC;IACnE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,oBAAoB,CAAC;IACpE,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAElD,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAA0B;YACrC,MAAM,QAAQ,GAAG,uBAAuB,CAAC,KAAK,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YACzE,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,KAAK,QAAQ;gBACzC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC;gBACtC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACvC,MAAM,MAAM,GAAmB,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;YACvE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAChC,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,IAAY;YACpB,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;QACnC,CAAC;QAED,KAAK,CAAC,IAAI;YACR,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CACnE,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export type ArtifactStubGuardMode = 'reject' | 'warn' | 'off';
|
|
2
|
+
export interface ArtifactStubGuardConfig {
|
|
3
|
+
readonly mode: ArtifactStubGuardMode;
|
|
4
|
+
readonly minRetainedRatio: number;
|
|
5
|
+
readonly minPriorBytes: number;
|
|
6
|
+
/** File-name extensions (including the leading dot) this guard scans for prior siblings, e.g. `['.html', '.htm']`. */
|
|
7
|
+
readonly siblingExtensions: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export interface PriorArtifactSibling {
|
|
10
|
+
readonly name: string;
|
|
11
|
+
readonly size: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ArtifactStubGuardWarning {
|
|
14
|
+
readonly code: 'ARTIFACT_REGRESSION';
|
|
15
|
+
readonly message: string;
|
|
16
|
+
readonly identifier: string;
|
|
17
|
+
readonly newSize: number;
|
|
18
|
+
readonly priorSize: number;
|
|
19
|
+
readonly priorName: string;
|
|
20
|
+
}
|
|
21
|
+
export interface EvaluateArtifactStubGuardInput {
|
|
22
|
+
readonly scanDir: string;
|
|
23
|
+
readonly identifier: string;
|
|
24
|
+
readonly newSize: number;
|
|
25
|
+
readonly config: ArtifactStubGuardConfig;
|
|
26
|
+
}
|
|
27
|
+
export interface EvaluateArtifactStubGuardResult {
|
|
28
|
+
readonly outcome: 'pass' | 'warn' | 'reject';
|
|
29
|
+
readonly warning?: ArtifactStubGuardWarning;
|
|
30
|
+
}
|
|
31
|
+
export declare class ArtifactRegressionError extends Error {
|
|
32
|
+
readonly code = "ARTIFACT_REGRESSION";
|
|
33
|
+
readonly identifier: string;
|
|
34
|
+
readonly newSize: number;
|
|
35
|
+
readonly priorSize: number;
|
|
36
|
+
readonly priorName: string;
|
|
37
|
+
constructor(message: string, details: {
|
|
38
|
+
identifier: string;
|
|
39
|
+
newSize: number;
|
|
40
|
+
priorSize: number;
|
|
41
|
+
priorName: string;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export declare const DEFAULT_ARTIFACT_STUB_GUARD_CONFIG: ArtifactStubGuardConfig;
|
|
45
|
+
/** Slugifies a free-form identifier into a filename-safe basename (lowercase, `[a-z0-9_-]`, max 60 chars). */
|
|
46
|
+
export declare function slugifyArtifactIdentifier(value: string): string;
|
|
47
|
+
/** Fallback basename used when a slugified identifier is empty (e.g. an all-non-ASCII identifier strips to nothing). */
|
|
48
|
+
export declare const EMPTY_SLUG_FALLBACK_NAME = "artifact";
|
|
49
|
+
/**
|
|
50
|
+
* Two identifiers refer to the same artifact lineage when they're literally
|
|
51
|
+
* equal, or one is the canonical slug form of the other (and that slug is
|
|
52
|
+
* non-empty). Slug equality alone is not enough: a slugifier that truncates
|
|
53
|
+
* at a fixed length would otherwise falsely bridge two distinct identifiers
|
|
54
|
+
* that only diverge past that length — requiring one side to *be* the slug
|
|
55
|
+
* form of the other avoids that while still bridging e.g. "Landing Page" and
|
|
56
|
+
* "landing-page".
|
|
57
|
+
*/
|
|
58
|
+
export declare function artifactIdentifiersMatch(a: string, b: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Finds prior siblings on disk that share an identifier with a newly-written
|
|
61
|
+
* artifact, matching `<identifier>(-N)?.<ext>` for any of `config.siblingExtensions`.
|
|
62
|
+
* The scan deliberately includes any file at the same path as the new write
|
|
63
|
+
* — when a write overwrites an existing file with the same name, the file
|
|
64
|
+
* currently on disk is the prior content (the overwrite happens after this
|
|
65
|
+
* scan runs).
|
|
66
|
+
*/
|
|
67
|
+
export declare function findPriorArtifactSiblings(scanDir: string, identifier: string, config: Pick<ArtifactStubGuardConfig, 'siblingExtensions'>): Promise<PriorArtifactSibling[]>;
|
|
68
|
+
/** Reads guard config from `ARTIFACT_STUB_GUARD` / `ARTIFACT_STUB_GUARD_MIN_RATIO` / `ARTIFACT_STUB_GUARD_MIN_PRIOR_BYTES`, falling back to `defaults` for any unset/invalid value. */
|
|
69
|
+
export declare function readArtifactStubGuardConfigFromEnv(env?: NodeJS.ProcessEnv, defaults?: ArtifactStubGuardConfig): ArtifactStubGuardConfig;
|
|
70
|
+
/** Pure decision function: given the prior siblings on disk, decides whether the new body is a stub regression. Split from the disk scan so unit tests stay fast and can pre-fetch siblings. */
|
|
71
|
+
export declare function classifyArtifactStubGuard(priors: readonly PriorArtifactSibling[], identifier: string, newSize: number, config: ArtifactStubGuardConfig): EvaluateArtifactStubGuardResult;
|
|
72
|
+
export declare function evaluateArtifactStubGuard(input: EvaluateArtifactStubGuardInput): Promise<EvaluateArtifactStubGuardResult>;
|
|
73
|
+
//# sourceMappingURL=stub-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stub-guard.d.ts","sourceRoot":"","sources":["../../src/artifacts/stub-guard.ts"],"names":[],"mappings":"AAwBA,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AAE9D,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,sHAAsH;IACtH,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;CAC1C;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,wBAAwB,CAAC;CAC7C;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,yBAAyB;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAGzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;CASzF;AAED,eAAO,MAAM,kCAAkC,EAAE,uBAKhD,CAAC;AAMF,8GAA8G;AAC9G,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/D;AAED,wHAAwH;AACxH,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAOtE;AA0BD;;;;;;;GAOG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,IAAI,CAAC,uBAAuB,EAAE,mBAAmB,CAAC,GACzD,OAAO,CAAC,oBAAoB,EAAE,CAAC,CA2CjC;AAED,uLAAuL;AACvL,wBAAgB,kCAAkC,CAChD,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,QAAQ,GAAE,uBAA4D,GACrE,uBAAuB,CAezB;AAiBD,gMAAgM;AAChM,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,oBAAoB,EAAE,EACvC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,uBAAuB,GAC9B,+BAA+B,CAqBjC;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,+BAA+B,CAAC,CAK1C"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export class ArtifactRegressionError extends Error {
|
|
4
|
+
code = 'ARTIFACT_REGRESSION';
|
|
5
|
+
identifier;
|
|
6
|
+
newSize;
|
|
7
|
+
priorSize;
|
|
8
|
+
priorName;
|
|
9
|
+
constructor(message, details) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'ArtifactRegressionError';
|
|
12
|
+
this.identifier = details.identifier;
|
|
13
|
+
this.newSize = details.newSize;
|
|
14
|
+
this.priorSize = details.priorSize;
|
|
15
|
+
this.priorName = details.priorName;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export const DEFAULT_ARTIFACT_STUB_GUARD_CONFIG = {
|
|
19
|
+
mode: 'warn',
|
|
20
|
+
minRetainedRatio: 0.2,
|
|
21
|
+
minPriorBytes: 4096,
|
|
22
|
+
siblingExtensions: ['.html', '.htm'],
|
|
23
|
+
};
|
|
24
|
+
function escapeRegExp(value) {
|
|
25
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
26
|
+
}
|
|
27
|
+
/** Slugifies a free-form identifier into a filename-safe basename (lowercase, `[a-z0-9_-]`, max 60 chars). */
|
|
28
|
+
export function slugifyArtifactIdentifier(value) {
|
|
29
|
+
return value
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/[^a-z0-9_-]+/g, '-')
|
|
32
|
+
.replace(/^-+|-+$/g, '')
|
|
33
|
+
.slice(0, 60);
|
|
34
|
+
}
|
|
35
|
+
/** Fallback basename used when a slugified identifier is empty (e.g. an all-non-ASCII identifier strips to nothing). */
|
|
36
|
+
export const EMPTY_SLUG_FALLBACK_NAME = 'artifact';
|
|
37
|
+
/**
|
|
38
|
+
* Two identifiers refer to the same artifact lineage when they're literally
|
|
39
|
+
* equal, or one is the canonical slug form of the other (and that slug is
|
|
40
|
+
* non-empty). Slug equality alone is not enough: a slugifier that truncates
|
|
41
|
+
* at a fixed length would otherwise falsely bridge two distinct identifiers
|
|
42
|
+
* that only diverge past that length — requiring one side to *be* the slug
|
|
43
|
+
* form of the other avoids that while still bridging e.g. "Landing Page" and
|
|
44
|
+
* "landing-page".
|
|
45
|
+
*/
|
|
46
|
+
export function artifactIdentifiersMatch(a, b) {
|
|
47
|
+
if (a === b)
|
|
48
|
+
return true;
|
|
49
|
+
const slugA = slugifyArtifactIdentifier(a);
|
|
50
|
+
if (slugA.length === 0)
|
|
51
|
+
return false;
|
|
52
|
+
const slugB = slugifyArtifactIdentifier(b);
|
|
53
|
+
if (slugA !== slugB)
|
|
54
|
+
return false;
|
|
55
|
+
return a === slugA || b === slugB;
|
|
56
|
+
}
|
|
57
|
+
async function readSidecarIdentifier(scanDir, entryName) {
|
|
58
|
+
try {
|
|
59
|
+
const raw = await readFile(path.join(scanDir, `${entryName}.artifact.json`), 'utf8');
|
|
60
|
+
const parsed = JSON.parse(raw);
|
|
61
|
+
const id = parsed?.metadata?.identifier;
|
|
62
|
+
return typeof id === 'string' && id.length > 0 ? id : null;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function extensionAlternation(extensions) {
|
|
69
|
+
return extensions.map((ext) => escapeRegExp(ext)).join('|');
|
|
70
|
+
}
|
|
71
|
+
function legacyCandidateIdentifiers(filename, extensionPattern, suffixPattern) {
|
|
72
|
+
const fullBasename = filename.replace(extensionPattern, '');
|
|
73
|
+
const stripped = filename.replace(suffixPattern, '');
|
|
74
|
+
const candidates = [];
|
|
75
|
+
if (fullBasename.length > 0)
|
|
76
|
+
candidates.push(fullBasename);
|
|
77
|
+
if (stripped.length > 0 && stripped !== fullBasename)
|
|
78
|
+
candidates.push(stripped);
|
|
79
|
+
return candidates;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Finds prior siblings on disk that share an identifier with a newly-written
|
|
83
|
+
* artifact, matching `<identifier>(-N)?.<ext>` for any of `config.siblingExtensions`.
|
|
84
|
+
* The scan deliberately includes any file at the same path as the new write
|
|
85
|
+
* — when a write overwrites an existing file with the same name, the file
|
|
86
|
+
* currently on disk is the prior content (the overwrite happens after this
|
|
87
|
+
* scan runs).
|
|
88
|
+
*/
|
|
89
|
+
export async function findPriorArtifactSiblings(scanDir, identifier, config) {
|
|
90
|
+
if (identifier.length === 0)
|
|
91
|
+
return [];
|
|
92
|
+
const extAlternation = extensionAlternation(config.siblingExtensions);
|
|
93
|
+
if (!extAlternation)
|
|
94
|
+
return [];
|
|
95
|
+
const extensionPattern = new RegExp(`(?:${extAlternation})$`, 'i');
|
|
96
|
+
const suffixPattern = new RegExp(`(?:-\\d+)?(?:${extAlternation})$`, 'i');
|
|
97
|
+
const tokens = new Set();
|
|
98
|
+
tokens.add(identifier);
|
|
99
|
+
const slug = slugifyArtifactIdentifier(identifier);
|
|
100
|
+
if (slug.length > 0)
|
|
101
|
+
tokens.add(slug);
|
|
102
|
+
else
|
|
103
|
+
tokens.add(EMPTY_SLUG_FALLBACK_NAME);
|
|
104
|
+
const alternation = Array.from(tokens, escapeRegExp).join('|');
|
|
105
|
+
const pattern = new RegExp(`^(?:${alternation})(?:-\\d+)?(?:${extAlternation})$`, 'i');
|
|
106
|
+
let entries;
|
|
107
|
+
try {
|
|
108
|
+
entries = await readdir(scanDir, { withFileTypes: true });
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
const results = [];
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
if (!entry.isFile())
|
|
116
|
+
continue;
|
|
117
|
+
if (!pattern.test(entry.name))
|
|
118
|
+
continue;
|
|
119
|
+
const sidecarIdentifier = await readSidecarIdentifier(scanDir, entry.name);
|
|
120
|
+
// `legacyCandidateIdentifiers` always returns at least one non-empty
|
|
121
|
+
// candidate here: `entry.name` already matched `pattern` above, which
|
|
122
|
+
// requires its basename to start with one of `tokens` (all non-empty),
|
|
123
|
+
// so an explicit `candidateIdentifiers.length === 0` guard would be
|
|
124
|
+
// dead code — an empty array's `.some()` below already `continue`s.
|
|
125
|
+
const candidateIdentifiers = sidecarIdentifier !== null
|
|
126
|
+
? [sidecarIdentifier]
|
|
127
|
+
: legacyCandidateIdentifiers(entry.name, extensionPattern, suffixPattern);
|
|
128
|
+
if (!candidateIdentifiers.some((c) => artifactIdentifiersMatch(identifier, c)))
|
|
129
|
+
continue;
|
|
130
|
+
try {
|
|
131
|
+
const st = await stat(path.join(scanDir, entry.name));
|
|
132
|
+
results.push({ name: entry.name, size: st.size });
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Ignore unreadable entries — they don't influence the guard decision.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return results;
|
|
139
|
+
}
|
|
140
|
+
/** Reads guard config from `ARTIFACT_STUB_GUARD` / `ARTIFACT_STUB_GUARD_MIN_RATIO` / `ARTIFACT_STUB_GUARD_MIN_PRIOR_BYTES`, falling back to `defaults` for any unset/invalid value. */
|
|
141
|
+
export function readArtifactStubGuardConfigFromEnv(env = process.env, defaults = DEFAULT_ARTIFACT_STUB_GUARD_CONFIG) {
|
|
142
|
+
const rawMode = (env.ARTIFACT_STUB_GUARD ?? '').toLowerCase();
|
|
143
|
+
const mode = rawMode === 'reject' || rawMode === 'warn' || rawMode === 'off' ? rawMode : defaults.mode;
|
|
144
|
+
const ratioRaw = Number(env.ARTIFACT_STUB_GUARD_MIN_RATIO);
|
|
145
|
+
// Accept (0, 1] so a caller can set 1 to reject any shrinkage. Values <=0 or >1 fall back to default.
|
|
146
|
+
const minRetainedRatio = Number.isFinite(ratioRaw) && ratioRaw > 0 && ratioRaw <= 1 ? ratioRaw : defaults.minRetainedRatio;
|
|
147
|
+
const minPriorBytesRaw = Number(env.ARTIFACT_STUB_GUARD_MIN_PRIOR_BYTES);
|
|
148
|
+
const minPriorBytes = Number.isInteger(minPriorBytesRaw) && minPriorBytesRaw > 0 ? minPriorBytesRaw : defaults.minPriorBytes;
|
|
149
|
+
return { mode, minRetainedRatio, minPriorBytes, siblingExtensions: defaults.siblingExtensions };
|
|
150
|
+
}
|
|
151
|
+
function buildWarning(identifier, newSize, prior) {
|
|
152
|
+
return {
|
|
153
|
+
code: 'ARTIFACT_REGRESSION',
|
|
154
|
+
message: `New artifact body for identifier "${identifier}" is ${newSize} bytes, ` +
|
|
155
|
+
`but the largest prior sibling "${prior.name}" is ${prior.size} bytes. ` +
|
|
156
|
+
'This pattern usually means the write emitted a placeholder instead of the full document. ' +
|
|
157
|
+
'Set the guard mode to "warn" to record the warning without rejecting, or "off" to disable the guard entirely.',
|
|
158
|
+
identifier,
|
|
159
|
+
newSize,
|
|
160
|
+
priorSize: prior.size,
|
|
161
|
+
priorName: prior.name,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/** Pure decision function: given the prior siblings on disk, decides whether the new body is a stub regression. Split from the disk scan so unit tests stay fast and can pre-fetch siblings. */
|
|
165
|
+
export function classifyArtifactStubGuard(priors, identifier, newSize, config) {
|
|
166
|
+
if (config.mode === 'off')
|
|
167
|
+
return { outcome: 'pass' };
|
|
168
|
+
if (identifier.length === 0)
|
|
169
|
+
return { outcome: 'pass' };
|
|
170
|
+
if (priors.length === 0)
|
|
171
|
+
return { outcome: 'pass' };
|
|
172
|
+
let largestSoFar = null;
|
|
173
|
+
for (const prior of priors) {
|
|
174
|
+
if (largestSoFar === null || prior.size > largestSoFar.size)
|
|
175
|
+
largestSoFar = prior;
|
|
176
|
+
}
|
|
177
|
+
// Non-null assertion, not a runtime guard: `priors.length === 0` already
|
|
178
|
+
// returned above, so this loop always runs at least once, and its first
|
|
179
|
+
// iteration always satisfies `largestSoFar === null` — it's always
|
|
180
|
+
// assigned by the time the loop finishes.
|
|
181
|
+
const largest = largestSoFar;
|
|
182
|
+
if (largest.size < config.minPriorBytes)
|
|
183
|
+
return { outcome: 'pass' };
|
|
184
|
+
const threshold = largest.size * config.minRetainedRatio;
|
|
185
|
+
if (newSize >= threshold)
|
|
186
|
+
return { outcome: 'pass' };
|
|
187
|
+
const warning = buildWarning(identifier, newSize, largest);
|
|
188
|
+
return { outcome: config.mode === 'reject' ? 'reject' : 'warn', warning };
|
|
189
|
+
}
|
|
190
|
+
export async function evaluateArtifactStubGuard(input) {
|
|
191
|
+
if (input.config.mode === 'off')
|
|
192
|
+
return { outcome: 'pass' };
|
|
193
|
+
if (input.identifier.length === 0)
|
|
194
|
+
return { outcome: 'pass' };
|
|
195
|
+
const priors = await findPriorArtifactSiblings(input.scanDir, input.identifier, input.config);
|
|
196
|
+
return classifyArtifactStubGuard(priors, input.identifier, input.newSize, input.config);
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=stub-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stub-guard.js","sourceRoot":"","sources":["../../src/artifacts/stub-guard.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,IAAI,MAAM,WAAW,CAAC;AAsC7B,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IACvC,IAAI,GAAG,qBAAqB,CAAC;IAC7B,UAAU,CAAS;IACnB,OAAO,CAAS;IAChB,SAAS,CAAS;IAClB,SAAS,CAAS;IAE3B,YACE,OAAe,EACf,OAAsF;QAEtF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACrC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,kCAAkC,GAA4B;IACzE,IAAI,EAAE,MAAM;IACZ,gBAAgB,EAAE,GAAG;IACrB,aAAa,EAAE,IAAI;IACnB,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;CACrC,CAAC;AAEF,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED,8GAA8G;AAC9G,MAAM,UAAU,yBAAyB,CAAC,KAAa;IACrD,OAAO,KAAK;SACT,WAAW,EAAE;SACb,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC;SAC7B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,wHAAwH;AACxH,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CAAC,CAAS,EAAE,CAAS;IAC3D,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,KAAK,GAAG,yBAAyB,CAAC,CAAC,CAAC,CAAC;IAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,KAAK,GAAG,yBAAyB,CAAC,CAAC,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAClC,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,KAAK,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,OAAe,EAAE,SAAiB;IACrE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,SAAS,gBAAgB,CAAC,EAAE,MAAM,CAAC,CAAC;QACrF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmD,CAAC;QACjF,MAAM,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC;QACxC,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,UAA6B;IACzD,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,0BAA0B,CAAC,QAAgB,EAAE,gBAAwB,EAAE,aAAqB;IACnG,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IACrD,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC3D,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,KAAK,YAAY;QAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChF,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,OAAe,EACf,UAAkB,EAClB,MAA0D;IAE1D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,MAAM,cAAc,GAAG,oBAAoB,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACtE,IAAI,CAAC,cAAc;QAAE,OAAO,EAAE,CAAC;IAC/B,MAAM,gBAAgB,GAAG,IAAI,MAAM,CAAC,MAAM,cAAc,IAAI,EAAE,GAAG,CAAC,CAAC;IACnE,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,gBAAgB,cAAc,IAAI,EAAE,GAAG,CAAC,CAAC;IAE1E,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACvB,MAAM,IAAI,GAAG,yBAAyB,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;;QACjC,MAAM,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IAC1C,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,OAAO,WAAW,iBAAiB,cAAc,IAAI,EAAE,GAAG,CAAC,CAAC;IAEvF,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YAAE,SAAS;QAC9B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QACxC,MAAM,iBAAiB,GAAG,MAAM,qBAAqB,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3E,qEAAqE;QACrE,sEAAsE;QACtE,uEAAuE;QACvE,oEAAoE;QACpE,oEAAoE;QACpE,MAAM,oBAAoB,GAAG,iBAAiB,KAAK,IAAI;YACrD,CAAC,CAAC,CAAC,iBAAiB,CAAC;YACrB,CAAC,CAAC,0BAA0B,CAAC,KAAK,CAAC,IAAI,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAC5E,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,wBAAwB,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;YAAE,SAAS;QACzF,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACtD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;QACzE,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uLAAuL;AACvL,MAAM,UAAU,kCAAkC,CAChD,MAAyB,OAAO,CAAC,GAAG,EACpC,WAAoC,kCAAkC;IAEtE,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9D,MAAM,IAAI,GACR,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;IAE5F,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IAC3D,sGAAsG;IACtG,MAAM,gBAAgB,GACpB,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,IAAI,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAEpG,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;IACzE,MAAM,aAAa,GACjB,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,IAAI,gBAAgB,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC;IAEzG,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,aAAa,EAAE,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB,EAAE,CAAC;AAClG,CAAC;AAED,SAAS,YAAY,CAAC,UAAkB,EAAE,OAAe,EAAE,KAA2B;IACpF,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EACL,qCAAqC,UAAU,QAAQ,OAAO,UAAU;YACxE,kCAAkC,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC,IAAI,UAAU;YACxE,2FAA2F;YAC3F,+GAA+G;QACjH,UAAU;QACV,OAAO;QACP,SAAS,EAAE,KAAK,CAAC,IAAI;QACrB,SAAS,EAAE,KAAK,CAAC,IAAI;KACtB,CAAC;AACJ,CAAC;AAED,gMAAgM;AAChM,MAAM,UAAU,yBAAyB,CACvC,MAAuC,EACvC,UAAkB,EAClB,OAAe,EACf,MAA+B;IAE/B,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACtD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACxD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAEpD,IAAI,YAAY,GAAgC,IAAI,CAAC;IACrD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,YAAY,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI;YAAE,YAAY,GAAG,KAAK,CAAC;IACpF,CAAC;IACD,yEAAyE;IACzE,wEAAwE;IACxE,mEAAmE;IACnE,0CAA0C;IAC1C,MAAM,OAAO,GAAyB,YAAa,CAAC;IACpD,IAAI,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,aAAa;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAEpE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,gBAAgB,CAAC;IACzD,IAAI,OAAO,IAAI,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAErD,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;AAC5E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,KAAqC;IAErC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAC5D,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAC9D,MAAM,MAAM,GAAG,MAAM,yBAAyB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9F,OAAO,yBAAyB,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;AAC1F,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module artifacts/text-suppression
|
|
3
|
+
*
|
|
4
|
+
* A streaming-text state machine that suppresses a tagged block (matched by
|
|
5
|
+
* caller-supplied open/close patterns) from user-visible output while it's
|
|
6
|
+
* still arriving — used to hide an artifact's raw markup from chat prose
|
|
7
|
+
* while it streams in, without buffering the whole response first. Ported
|
|
8
|
+
* from OD's `apps/daemon/src/artifacts/text-suppression.ts`.
|
|
9
|
+
*
|
|
10
|
+
* The core (`createTaggedTextSuppressor`) was already fully generic in the
|
|
11
|
+
* origin — it takes its open/close regexes and open/close prefix-match
|
|
12
|
+
* predicates as parameters, no product coupling. What needed de-branding
|
|
13
|
+
* were the origin's two pre-built instances: `createDsmlArtifactTextSuppressor`
|
|
14
|
+
* hardcoded OD's own "DSML" markup-language tag name family
|
|
15
|
+
* (`<|DSML artifact>`/`<artifact>`) as its open/close patterns.
|
|
16
|
+
* `createXmlTagTextSuppressor` below replaces it — a generic factory taking
|
|
17
|
+
* the caller's own tag-name list instead of a hardcoded vocabulary. The
|
|
18
|
+
* `tool_call`/`edit` pairing the origin's other instance
|
|
19
|
+
* (`createToolCallTextSuppressor`) suppressed is a generic agent-protocol
|
|
20
|
+
* convention, not OD-branded, and is kept as a named default instance.
|
|
21
|
+
*/
|
|
22
|
+
export type StreamTextEvent = {
|
|
23
|
+
readonly type: 'text_delta';
|
|
24
|
+
readonly delta: string;
|
|
25
|
+
};
|
|
26
|
+
export type StreamEventSink = (event: StreamTextEvent) => void;
|
|
27
|
+
export interface ArtifactTextSuppressor {
|
|
28
|
+
strip(text: string): string;
|
|
29
|
+
flush(): string;
|
|
30
|
+
isSuppressing(): boolean;
|
|
31
|
+
hasPendingCandidate(): boolean;
|
|
32
|
+
stats(): ArtifactTextSuppressorStats;
|
|
33
|
+
}
|
|
34
|
+
export interface ArtifactTextSuppressorStats {
|
|
35
|
+
readonly suppressedChars: number;
|
|
36
|
+
readonly suppressedChunks: number;
|
|
37
|
+
readonly openedBlocks: number;
|
|
38
|
+
readonly closedBlocks: number;
|
|
39
|
+
readonly pendingCandidateChars: number;
|
|
40
|
+
readonly suppressing: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Builds a suppressor for a tagged block delimited by `openRe`/`closeRe`.
|
|
44
|
+
* `isPossibleOpen`/`isPossibleClose` decide whether a trailing `<...`
|
|
45
|
+
* fragment (with no closing `>` yet) *could* still become a match once more
|
|
46
|
+
* text arrives — this is what lets the suppressor hold back a
|
|
47
|
+
* not-yet-decidable tail across streaming chunk boundaries instead of
|
|
48
|
+
* either emitting a tag fragment early or hanging indefinitely on a `<` that
|
|
49
|
+
* turns out to be unrelated text.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createTaggedTextSuppressor(args: {
|
|
52
|
+
openRe: RegExp;
|
|
53
|
+
closeRe: RegExp;
|
|
54
|
+
isPossibleOpen: (text: string) => boolean;
|
|
55
|
+
isPossibleClose: (text: string) => boolean;
|
|
56
|
+
}): ArtifactTextSuppressor;
|
|
57
|
+
/** Feeds `text` through `suppressor` and emits a `text_delta` on `onEvent` only when something survives. Returns whether an event was emitted. */
|
|
58
|
+
export declare function emitWithTextSuppressor(suppressor: ArtifactTextSuppressor, onEvent: StreamEventSink, text: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Builds a suppressor for an XML-like `<tagName ...>...</tagName>` block (or
|
|
61
|
+
* a bracket-pipe variant `<|tagName ...>...<|/tagName|>`), matched
|
|
62
|
+
* case-insensitively against any of `tagNames`. Replaces the origin's
|
|
63
|
+
* hardcoded "DSML" tag-name family with a caller-supplied vocabulary.
|
|
64
|
+
*/
|
|
65
|
+
export declare function createXmlTagTextSuppressor(tagNames: readonly string[]): ArtifactTextSuppressor;
|
|
66
|
+
/** Suppresses a `<tool_call>...</tool_call>` or `<edit>...</edit>` block — a generic agent-protocol convention, not product-branded. */
|
|
67
|
+
export declare function createToolCallTextSuppressor(): ArtifactTextSuppressor;
|
|
68
|
+
//# sourceMappingURL=text-suppression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-suppression.d.ts","sourceRoot":"","sources":["../../src/artifacts/text-suppression.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACtF,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAE/D,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,KAAK,IAAI,MAAM,CAAC;IAChB,aAAa,IAAI,OAAO,CAAC;IACzB,mBAAmB,IAAI,OAAO,CAAC;IAC/B,KAAK,IAAI,2BAA2B,CAAC;CACtC;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAID;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1C,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C,GAAG,sBAAsB,CAgFzB;AAED,kJAAkJ;AAClJ,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,sBAAsB,EAClC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,MAAM,GACX,OAAO,CAKT;AAqBD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,sBAAsB,CAmB9F;AAoBD,wIAAwI;AACxI,wBAAgB,4BAA4B,IAAI,sBAAsB,CAOrE"}
|