@makaio/extension-artifact-patch 1.0.0-dev-1789138982585 → 1.0.0-dev-1789148673705
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/patch-artifact.d.ts +30 -5
- package/dist/server.d.ts +1 -1
- package/dist/server.mjs +1 -1
- package/dist/src-DQbedDbK.mjs +1 -0
- package/dist/toolset.d.ts +1 -0
- package/package.json +1 -1
- package/dist/src-DQhu5uYZ.mjs +0 -1
package/README.md
CHANGED
|
@@ -57,8 +57,13 @@ through a filter can land somewhere the caller never addressed. Field match stil
|
|
|
57
57
|
to repeat blindly.
|
|
58
58
|
|
|
59
59
|
A rejected write is a separate case from a refused one. A `store` that returns a conflict persisted
|
|
60
|
-
nothing. A
|
|
61
|
-
|
|
60
|
+
nothing. A host that refuses before any effect — its own write validator inspecting the payload —
|
|
61
|
+
returns a structured rejection instead of throwing; the tool reports it as `STORE_REJECTED` with the
|
|
62
|
+
host's message and per-path issues, and its repair promises nothing was persisted, so the caller
|
|
63
|
+
corrects the input against the same base revision. A refusal that follows side-effecting steps must
|
|
64
|
+
stay a throw: the safe-resend promise only holds when the whole store attempt left no trace. A `store` that throws leaves the outcome unknown
|
|
65
|
+
— the contract covers the compare-and-swap, not what a throw means — so that repair hint asks for a
|
|
66
|
+
re-read instead of promising a safe retry.
|
|
62
67
|
|
|
63
68
|
Every rejection names the failing path and a repair hint; schema rejections add the expected type or
|
|
64
69
|
the allowed values per path. `dryRun` applies and validates without persisting.
|
|
@@ -97,7 +102,10 @@ the caller named them — an object to replace the rendering hints wholesale, `n
|
|
|
97
102
|
and must carry the previous revision's hints over when the property is absent, because hints are
|
|
98
103
|
caller-authored and the engine cannot tell whether the change made them stale. `store` also receives
|
|
99
104
|
the `schemaVersion` the payload was validated against and must persist the revision at that version,
|
|
100
|
-
which differs from `previous.schemaVersion` exactly when the request migrated the artifact.
|
|
105
|
+
which differs from `previous.schemaVersion` exactly when the request migrated the artifact. `store`
|
|
106
|
+
reports its outcome as the persisted revision, a conflict, or a structured rejection for a refusal
|
|
107
|
+
it makes before writing anything; only a failure whose outcome is genuinely unknown should throw.
|
|
108
|
+
The package never issues raw Artifact bus requests and
|
|
101
109
|
never reaches a store directly, so a service handling `artifact.patch` and the `artifacts_patch` MCP
|
|
102
110
|
tool run the same engine over the same contract. Its default package marker contributes no tools until a host is
|
|
103
111
|
explicitly bound.
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,6 @@ export default artifactPatchPackage;
|
|
|
13
13
|
export { applyArtifactPatch } from './patch-engine.js';
|
|
14
14
|
export type { ArtifactPatchApplication, ArtifactPatchApplicationResult } from './patch-engine.js';
|
|
15
15
|
export { executePatchArtifact, patchArtifact } from './patch-artifact.js';
|
|
16
|
-
export type { ArtifactPatchHost, ArtifactPatchStoreConflict, ArtifactPatchStoreRequest, ArtifactPatchStoreResult, } from './patch-artifact.js';
|
|
16
|
+
export type { ArtifactPatchHost, ArtifactPatchStoreConflict, ArtifactPatchStoreRejection, ArtifactPatchStoreRequest, ArtifactPatchStoreResult, } from './patch-artifact.js';
|
|
17
17
|
export { createArtifactPatchToolset, createPatchArtifactTool } from './toolset.js';
|
|
18
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./src-
|
|
1
|
+
import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./src-DQbedDbK.mjs";export{a as applyArtifactPatch,o as artifactPatchPackage,o as default,n as createArtifactPatchPackage,i as createArtifactPatchToolset,t as createPatchArtifactTool,e as executePatchArtifact,r as patchArtifact};
|
package/dist/patch-artifact.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ArtifactKindRegistration, type ArtifactPatchRequest, type ArtifactPatchResponse, type ArtifactRepresentations, type ArtifactRevision } from '@makaio/framework/contracts';
|
|
1
|
+
import { type ArtifactKindRegistration, type ArtifactPatchIssue, type ArtifactPatchRequest, type ArtifactPatchResponse, type ArtifactRepresentations, type ArtifactRevision } from '@makaio/framework/contracts';
|
|
2
2
|
import { type ToolExecutionContext, type ToolResult } from '@makaio/framework/tools';
|
|
3
3
|
/** One new revision the host is asked to persist. */
|
|
4
4
|
export interface ArtifactPatchStoreRequest {
|
|
@@ -44,8 +44,27 @@ export interface ArtifactPatchStoreConflict {
|
|
|
44
44
|
/** Revision the artifact carries instead of `previous.revision`. */
|
|
45
45
|
readonly conflictingRevision: string;
|
|
46
46
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The host refused the write before writing anything; nothing was persisted.
|
|
49
|
+
*
|
|
50
|
+
* This is the return shape for a refusal the host makes before any effect — a
|
|
51
|
+
* write validator that inspects the payload and touches nothing. A refusal
|
|
52
|
+
* that surfaces after side-effecting steps ran (a later lifecycle hook
|
|
53
|
+
* rejecting after earlier hooks acted) must stay a throw, because "resend the
|
|
54
|
+
* corrected patch" is only safe when the whole store attempt left no trace.
|
|
55
|
+
* One message plus optional per-path issues, no error taxonomy: the caller's
|
|
56
|
+
* next step is the same for every side-effect-free refusal.
|
|
57
|
+
*/
|
|
58
|
+
export interface ArtifactPatchStoreRejection {
|
|
59
|
+
readonly rejection: {
|
|
60
|
+
/** Why the host refused the write. */
|
|
61
|
+
readonly message: string;
|
|
62
|
+
/** Per-path rejections, when the refusal names locations in `data`. */
|
|
63
|
+
readonly issues?: readonly ArtifactPatchIssue[];
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** The persisted revision, the conflict that stopped it, or the host's refusal. */
|
|
67
|
+
export type ArtifactPatchStoreResult = ArtifactRevision | ArtifactPatchStoreConflict | ArtifactPatchStoreRejection;
|
|
49
68
|
/**
|
|
50
69
|
* Host-owned access boundary for patch-based Artifact revisions.
|
|
51
70
|
*
|
|
@@ -83,8 +102,14 @@ export interface ArtifactPatchHost {
|
|
|
83
102
|
*
|
|
84
103
|
* A thrown rejection is reported to the caller as an unknown outcome, because
|
|
85
104
|
* this contract cannot tell a write that never ran from one that committed
|
|
86
|
-
* before the failure surfaced.
|
|
87
|
-
*
|
|
105
|
+
* before the failure surfaced. A refusal the host makes before any effect —
|
|
106
|
+
* its own write validator inspecting the payload — is returned as
|
|
107
|
+
* `ArtifactPatchStoreRejection` instead of thrown, so the caller learns that
|
|
108
|
+
* nothing was persisted and what to correct. Return it only when the whole
|
|
109
|
+
* store attempt is known to be side-effect-free: a rejection that follows
|
|
110
|
+
* side-effecting steps (a later lifecycle hook refusing after earlier hooks
|
|
111
|
+
* acted) must stay a throw. `ArtifactPatchStoreConflict` remains the refusal
|
|
112
|
+
* for a concurrent revision; both returned refusals promise nothing was
|
|
88
113
|
* persisted.
|
|
89
114
|
*
|
|
90
115
|
* The new revision is stored at `request.schemaVersion`, which is the version
|
package/dist/server.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { default, applyArtifactPatch, artifactPatchPackage, createArtifactPatchPackage, createArtifactPatchToolset, createPatchArtifactTool, executePatchArtifact, patchArtifact, } from './index.js';
|
|
2
|
-
export type { ArtifactPatchApplication, ArtifactPatchApplicationResult, ArtifactPatchHost, ArtifactPatchStoreConflict, ArtifactPatchStoreRequest, ArtifactPatchStoreResult, } from './index.js';
|
|
2
|
+
export type { ArtifactPatchApplication, ArtifactPatchApplicationResult, ArtifactPatchHost, ArtifactPatchStoreConflict, ArtifactPatchStoreRejection, ArtifactPatchStoreRequest, ArtifactPatchStoreResult, } from './index.js';
|
|
3
3
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./src-
|
|
1
|
+
import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./src-DQbedDbK.mjs";export{a as applyArtifactPatch,o as artifactPatchPackage,o as default,n as createArtifactPatchPackage,i as createArtifactPatchToolset,t as createPatchArtifactTool,e as executePatchArtifact,r as patchArtifact};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ARTIFACT_COLLECTION_ELEMENT_SEGMENT as e,ArtifactPatchIssueSchema as t,ArtifactPatchRequestSchema as n,ArtifactPatchResponseSchema as r,artifactPatchFilterName as i,artifactPatchInstructions as a,artifactPatchSegments as o,compileArtifactDataChecker as s,defineOwnValue as c,inspectArtifactDataLocation as l,isJsonObject as u,jsonEquals as d,ownValue as f,readArtifactTitle as p,readPropertyPath as m}from"@makaio/framework/contracts";import{ToolErrorCodes as h,defineTool as g,defineToolset as _,toolError as v,toolSuccess as ee,widenTool as te}from"@makaio/framework/tools";function y(t){return t.map(t=>t.kind===`property`?t.name:e)}function b(e){return e.length>0&&e.every(e=>typeof e!=`boolean`&&e.type===`array`)}function ne(e){return Object.entries(e).map(([e,t])=>({path:e.split(`.`).slice(1),operand:t}))}function x(e){let t=new Map;for(let n of e.arrayFilters??[]){let e=i(n);e!==void 0&&t.set(e,ne(n))}return t}function S(e,t){return t.every(({path:t,operand:n})=>d(m(e,t),n))}function C(e,t){return u(t)?u(e)?Object.entries(t).every(([t,n])=>d(f(e,t),n)):!1:d(e,t)}function w(e,t,n,r){let i=[];for(let a of e){if(t.kind===`property`){if(!u(a)||r&&!Object.hasOwn(a,t.name))continue;i.push({kind:`property`,container:a,key:t.name});continue}if(!Array.isArray(a))continue;if(t.kind===`index`){t.index<a.length&&i.push({kind:`element`,container:a,index:t.index});continue}let e=n.get(t.placeholder)??[];a.forEach((t,n)=>{S(t,e)&&i.push({kind:`element`,container:a,index:n})})}return i}function T(e){return e.kind===`property`?f(e.container,e.key):e.container[e.index]}function E(e,t){e.kind===`property`?c(e.container,e.key,t):e.container[e.index]=t}function D(e,t,n){let r=[e];for(let e of t){let t=w(r,e,n,!0);if(t.length===0||e.kind!==`filter`&&t.length<r.length)return{ok:!1,segment:e};r=t.map(T)}return{ok:!0,containers:r}}function re(e){return e.kind===`property`?e.name:e.kind===`index`?String(e.index):`$[${e.placeholder}]`}function O(e,t,n,r,i){return{code:e,message:r,operator:t,path:n,repair:i}}function k(e,t,n){return O(`NO_MATCH`,e,t,n,`Addressing nothing is a failure, not a silent no-op: check the match values against the current revision.`)}function ie(e,t){for(let n of e)E(n,structuredClone(t));return e.length}function A(e){for(let t of e)t.kind===`property`&&Reflect.deleteProperty(t.container,t.key);return e.length}function j(e,t){for(let n of e){let e=T(n);if(e===void 0){E(n,[structuredClone(t)]);continue}if(!Array.isArray(e))return;e.push(structuredClone(t))}return e.length}function M(e,t){let n=0;for(let r of e){let e=T(r);if(!Array.isArray(e))return;let i=e.filter(e=>!C(e,t));n+=e.length-i.length,e.splice(0,e.length,...i)}return n}function N(e,t,n){switch(e){case`$set`:return ie(t,n);case`$unset`:return A(t);case`$push`:return j(t,n);case`$pull`:return M(t,n)}}function P(e,t,n){if(t.kind!==`property`){if(e===`$unset`)return O(`UNSUPPORTED_TARGET`,e,n,`$unset addresses object properties; '${n}' addresses a collection entry.`,`Remove a collection entry with $pull instead of $unset.`);if(e===`$push`||e===`$pull`)return O(`UNSUPPORTED_TARGET`,e,n,`${e} addresses a collection; '${n}' addresses one of its entries.`,`Point ${e} at the collection itself, without the trailing entry selector.`)}}function F(e,t,n,r){let i=l(e,y(r));if(i===void 0)return O(`PATH_NOT_DECLARED`,t,n,`The artifact kind does not declare '${n}'.`,`Correct the path to one the kind schema declares; a misspelled field is never created.`);if((t===`$push`||t===`$pull`)&&!b(i))return O(`TARGET_NOT_A_COLLECTION`,t,n,`The artifact kind declares '${n}' as something other than a collection.`,`Use $set to replace '${n}', or point ${t} at a declared collection.`)}function I(e,t,n,r,i){let{operator:a,path:s,value:c}=t,l=o(s),u=l.at(-1);if(u===void 0)return k(a,s,`'${s}' addresses nothing.`);let d=P(a,u,s);if(d)return d;let f=F(r,a,s,l),p=i.allowUndeclaredRemovals===!0&&f?.code===`PATH_NOT_DECLARED`&&a===`$unset`;if(f&&!p)return f;let m=D(e,l.slice(0,-1),n);if(!m.ok){let e=re(m.segment);return m.segment.kind===`property`?O(`PATH_NOT_RESOLVABLE`,a,s,`This revision has no value at '${e}' along '${s}'.`,`Set the value at '${e}' before addressing anything below it.`):k(a,s,`'${e}' in '${s}' addressed no entry in this revision.`)}let h=a===`$unset`||a===`$pull`,g=w(m.containers,u,n,h);if(g.length===0)return k(a,s,`'${s}' addressed no entry in this revision.`);if(u.kind===`index`&&g.length<m.containers.length)return k(a,s,`'${s}' addressed no entry in every selected collection of this revision.`);let _=N(a,g,c);return _===void 0?O(`TARGET_NOT_A_COLLECTION`,a,s,`The value at '${s}' is not a collection in this revision.`,`Use $set to replace '${s}', or point ${a} at a collection.`):_===0?k(a,s,`'${s}' changed nothing in this revision.`):{operator:a,path:s,matched:_}}function L(e,t,n,r={}){let i=structuredClone(e),o=x(t),s=[];for(let e of a(t)){let t=I(i,e,o,n,r);if(`code`in t)return{ok:!1,error:t};s.push(t)}return{ok:!0,application:{data:i,operations:s}}}function R(e){return`conflictingRevision`in e}function z(e){return`rejection`in e}function B(e){let n=(e.rejection.issues??[]).map(e=>t.safeParse(e)).flatMap(e=>e.success?[e.data]:[]),r=e.rejection.message.trim();return{code:`STORE_REJECTED`,message:r===``?`The host refused the write without naming a reason.`:`The host refused the write: ${r}`,...n.length===0?{}:{issues:n},repair:`Nothing was persisted. Correct what the rejection names and resend the patch against the same baseRevision.`}}function V(e){return e instanceof Error?e.message:String(e)}function H(e){return{ok:!1,error:e}}function U(e,t,n){return{refClass:`artifact`,kind:e,id:t,revision:n}}function W(e){return{path:e.path,reason:e.reason,...e.expectedType===void 0?{}:{expectedType:e.expectedType},...e.allowedValues===void 0?{}:{allowedValues:[...e.allowedValues]}}}function G(e){return e.representations!==void 0||e.schemaVersion!==void 0?!1:a(e.patch).every(({operator:e,path:t})=>e===`$push`&&o(t).every(e=>e.kind===`property`))}function K(e,t){return{code:`BASE_REVISION_CONFLICT`,message:`Artifact '${e.ref.kind}:${e.ref.id}' has advanced to revision '${t}'.`,currentRevision:t,repair:G(e)?`Resend the same patch with baseRevision '${t}'; it only appends at a fixed path, with no position and no filter, so it does not depend on the payload you read.`:`Re-read the artifact at revision '${t}' and rewrite the patch: only an append at a fixed path survives a concurrent write, with no position and no filter. Replacing, removing, addressing an entry by position, appending through a $[filter] placeholder, replacing or clearing representations, and naming a target schemaVersion all depend on the payload you read, which the concurrent revision may have changed.`}}function q(e){let t=e[0];if(!t)return`Correct the patched result so it satisfies the kind schema.`;let n=t.path===``?`the payload root`:`'${t.path}'`;return t.allowedValues?`${n} accepts one of: ${t.allowedValues.map(e=>typeof e==`string`?e:JSON.stringify(e)).join(`, `)}.`:t.expectedType?`${n} expects type ${t.expectedType}.`:`${n} ${t.reason}.`}function J(e,t){try{p(e,t.titlePath);return}catch(e){return{code:`SCHEMA_VALIDATION_FAILED`,message:`The patched result does not satisfy the '${t.kind}' data schema.`,issues:[{path:t.titlePath,reason:V(e)}],repair:`'${t.titlePath}' must be a nonblank string.`}}}function ae(e){try{return s(e)}catch(t){return{code:`HOST_FAILED`,message:`Artifact kind '${e.kind}' could not compile its data schema: ${V(t)}`,repair:`Correct the registered data schema before patching artifacts of this kind.`}}}function oe(e,t,n){let r=e.filter(e=>e.kind===t.kind);if(r.length===0)return{code:`KIND_NOT_REGISTERED`,message:`Artifact kind '${t.kind}' is not registered.`,repair:`Register the kind, or address an artifact of a registered kind.`};let i=r.find(e=>e.schemaVersion===n);if(!i){let e=[...new Set(r.map(e=>e.schemaVersion))].sort((e,t)=>e-t),i=e.join(`, `),a=n===t.schemaVersion?`Revision '${t.revision}' uses schema version ${n}`:`The request targets schema version ${n} for revision '${t.revision}' (schema version ${t.schemaVersion})`,o=e.filter(e=>e>t.schemaVersion).join(`, `);return{code:`SCHEMA_VERSION_MISMATCH`,message:`${a}, for which '${t.kind}' has no registration; registered: ${i}.`,repair:o===``?`Register '${t.kind}' at schema version ${n}${n>t.schemaVersion?``:` or newer`}; no registered version is newer than the revision's.`:`Set schemaVersion to one of ${o} and add the instructions that make the payload fit that version, or register '${t.kind}' at schema version ${n}.`}}return i}async function se(e,t,n){let r;try{r=await n.resolveCurrent({kind:e.ref.kind,id:e.ref.id},t)}catch(e){return{code:`HOST_FAILED`,message:`Artifact lookup failed: ${V(e)}`,repair:`Retry once the artifact store is reachable.`}}return r?r.kind!==e.ref.kind||r.id!==e.ref.id?{code:`HOST_FAILED`,message:`Artifact lookup returned a different artifact identity.`,repair:`Retry; the resolved artifact did not match the requested identity.`}:r.revision===e.baseRevision?r:K(e,r.revision):{code:`ARTIFACT_NOT_FOUND`,message:`Artifact '${e.ref.kind}:${e.ref.id}' was not found.`,repair:`Check the kind and identity, or create the artifact before revising it.`}}async function Y(e,t,n){let r=await se(e,t,n);if(`code`in r)return H(r);let i=U(r.kind,r.id,r.revision),a;try{a=await n.listKinds(e.ref.kind,t)}catch(e){return H({code:`HOST_FAILED`,message:`Artifact kind lookup failed: ${V(e)}`,repair:`Retry once the kind catalog is reachable.`})}let o=ce(e,r,a);if(`code`in o)return H(o);let s=o.schemaVersion!==r.schemaVersion,c=ae(o);if(typeof c!=`function`)return H(c);let l=L(r.data,e.patch,o.dataSchema,{allowUndeclaredRemovals:s});if(!l.ok)return H(l.error);let u=c(l.application.data);if(!u.valid)return H({code:`SCHEMA_VALIDATION_FAILED`,message:`The patched result does not satisfy the '${o.kind}' data schema.`,issues:u.issues.map(W),repair:q(u.issues)});let d=J(l.application.data,o);if(d)return H(d);let f=[...l.application.operations],p=s?{migration:{from:r.schemaVersion,to:o.schemaVersion}}:{};if(e.dryRun===!0)return{ok:!0,base:i,dryRun:!0,operations:f,...p};let m;try{m=await n.store({previous:r,data:l.application.data,schemaVersion:o.schemaVersion,...e.statusPath===void 0?{}:{statusPath:e.statusPath},...e.representations===void 0?{}:{representations:e.representations}},t)}catch(e){return H({code:`HOST_FAILED`,message:`Artifact revision failed: ${V(e)}`,repair:`Re-read the artifact: the write may have been committed before the failure was reported. Retry only after confirming the change is absent.`})}if(R(m))return H(K(e,m.conflictingRevision));if(z(m))return H(B(m));let h=le(m,i,o.schemaVersion);return h?H(h):{ok:!0,base:i,dryRun:!1,artifact:U(m.kind,m.id,m.revision),operations:f,...p}}function ce(e,t,n){if(e.schemaVersion!==void 0&&e.schemaVersion<t.schemaVersion)return{code:`SCHEMA_VERSION_MISMATCH`,message:`The request targets schema version ${e.schemaVersion}, older than revision '${t.revision}' (schema version ${t.schemaVersion}); a migration never moves an artifact back.`,repair:`Omit schemaVersion to patch at version ${t.schemaVersion}, or name a newer registered version to migrate the artifact forward.`};let r=oe(n,t,e.schemaVersion??t.schemaVersion);return`code`in r?r:r.schemaVersion===t.schemaVersion&&a(e.patch).length===0?{code:`NO_CHANGE`,message:`The patch carries no instruction and targets schema version ${r.schemaVersion}, which revision '${t.revision}' already has.`,repair:`Add at least one instruction, or name a different schemaVersion to migrate the artifact.`}:r}function le(e,t,n){if(e.kind!==t.kind||e.id!==t.id||e.revision===t.revision)return{code:`HOST_FAILED`,message:`The store returned an artifact that is not a new revision of the patched one.`,repair:`Re-read the artifact before patching it again; the write outcome is unclear.`};if(e.schemaVersion!==n)return{code:`HOST_FAILED`,message:`The store persisted revision '${e.revision}' at schema version ${e.schemaVersion} instead of ${n}.`,repair:`Re-read the artifact; the host did not store the revision at the schema version the patch was validated against.`}}async function X(e,t,n){return n?ee(await Y(e,t,n)):v(h.PERMISSION_DENIED,`Artifact revisions require an authorized host.`)}function Z(e){return g({name:`artifacts_patch`,description:`Revise an Artifact by sending only the change. Name the artifact, the baseRevision you read, and the instructions: $set and $unset on declared paths, $push and $pull on declared collections. Address one collection entry by field match with $[name] plus arrayFilters, or by position. An unknown path, an unknown operator and addressing nothing are all errors, never silent no-ops. Use dryRun to check a patch without writing. A stale baseRevision reports the current revision; follow the repair field of that error: only an append at a fixed path (no position, no filter) may be resent with the new baseRevision, anything else needs a fresh read and a rewritten patch. Rendering hints are separate from data: omit representations to keep them, send an object to replace all of them (no merge), or null to clear them. Name schemaVersion to migrate an artifact left at an older version by a kind bump: the patched result is validated against that version's registration and stored at it; omit it to keep the base revision's version. A migration may $unset a property the target no longer declares, and may carry no instruction when the payload already fits.`,annotations:{readOnly:!1,idempotent:!1},inputSchema:n,outputSchema:r,execute:(t,n)=>X(t,n,e)})}function Q(e){return _({name:`artifact-patch`,description:`Revise Artifacts by patch through an authorized host.`,version:`0.1.0`,tools:[te(Z(e))]})}function $(e){return{name:`artifact-patch`,displayName:`Artifact Patch Tools`,version:`0.1.0`,surface:`headless`,tools:{createToolsets:()=>e?[Q(e)]:[]}}}const ue=$();export{X as a,Z as i,$ as n,Y as o,Q as r,L as s,ue as t};
|
package/dist/toolset.d.ts
CHANGED
|
@@ -95,6 +95,7 @@ export declare function createPatchArtifactTool(host: ArtifactPatchHost): import
|
|
|
95
95
|
PATH_NOT_RESOLVABLE: "PATH_NOT_RESOLVABLE";
|
|
96
96
|
SCHEMA_VALIDATION_FAILED: "SCHEMA_VALIDATION_FAILED";
|
|
97
97
|
SCHEMA_VERSION_MISMATCH: "SCHEMA_VERSION_MISMATCH";
|
|
98
|
+
STORE_REJECTED: "STORE_REJECTED";
|
|
98
99
|
TARGET_NOT_A_COLLECTION: "TARGET_NOT_A_COLLECTION";
|
|
99
100
|
UNSUPPORTED_TARGET: "UNSUPPORTED_TARGET";
|
|
100
101
|
}>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makaio/extension-artifact-patch",
|
|
3
3
|
"description": "Patch-based artifact revision tools for AI agents.",
|
|
4
|
-
"version": "1.0.0-dev-
|
|
4
|
+
"version": "1.0.0-dev-1789148673705",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
package/dist/src-DQhu5uYZ.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ARTIFACT_COLLECTION_ELEMENT_SEGMENT as e,ArtifactPatchRequestSchema as t,ArtifactPatchResponseSchema as n,artifactPatchFilterName as r,artifactPatchInstructions as i,artifactPatchSegments as a,compileArtifactDataChecker as o,defineOwnValue as s,inspectArtifactDataLocation as c,isJsonObject as l,jsonEquals as u,ownValue as d,readArtifactTitle as f,readPropertyPath as p}from"@makaio/framework/contracts";import{ToolErrorCodes as m,defineTool as h,defineToolset as g,toolError as _,toolSuccess as v,widenTool as y}from"@makaio/framework/tools";function ee(t){return t.map(t=>t.kind===`property`?t.name:e)}function te(e){return e.length>0&&e.every(e=>typeof e!=`boolean`&&e.type===`array`)}function b(e){return Object.entries(e).map(([e,t])=>({path:e.split(`.`).slice(1),operand:t}))}function x(e){let t=new Map;for(let n of e.arrayFilters??[]){let e=r(n);e!==void 0&&t.set(e,b(n))}return t}function S(e,t){return t.every(({path:t,operand:n})=>u(p(e,t),n))}function C(e,t){return l(t)?l(e)?Object.entries(t).every(([t,n])=>u(d(e,t),n)):!1:u(e,t)}function w(e,t,n,r){let i=[];for(let a of e){if(t.kind===`property`){if(!l(a)||r&&!Object.hasOwn(a,t.name))continue;i.push({kind:`property`,container:a,key:t.name});continue}if(!Array.isArray(a))continue;if(t.kind===`index`){t.index<a.length&&i.push({kind:`element`,container:a,index:t.index});continue}let e=n.get(t.placeholder)??[];a.forEach((t,n)=>{S(t,e)&&i.push({kind:`element`,container:a,index:n})})}return i}function T(e){return e.kind===`property`?d(e.container,e.key):e.container[e.index]}function E(e,t){e.kind===`property`?s(e.container,e.key,t):e.container[e.index]=t}function D(e,t,n){let r=[e];for(let e of t){let t=w(r,e,n,!0);if(t.length===0||e.kind!==`filter`&&t.length<r.length)return{ok:!1,segment:e};r=t.map(T)}return{ok:!0,containers:r}}function ne(e){return e.kind===`property`?e.name:e.kind===`index`?String(e.index):`$[${e.placeholder}]`}function O(e,t,n,r,i){return{code:e,message:r,operator:t,path:n,repair:i}}function k(e,t,n){return O(`NO_MATCH`,e,t,n,`Addressing nothing is a failure, not a silent no-op: check the match values against the current revision.`)}function re(e,t){for(let n of e)E(n,structuredClone(t));return e.length}function ie(e){for(let t of e)t.kind===`property`&&Reflect.deleteProperty(t.container,t.key);return e.length}function A(e,t){for(let n of e){let e=T(n);if(e===void 0){E(n,[structuredClone(t)]);continue}if(!Array.isArray(e))return;e.push(structuredClone(t))}return e.length}function j(e,t){let n=0;for(let r of e){let e=T(r);if(!Array.isArray(e))return;let i=e.filter(e=>!C(e,t));n+=e.length-i.length,e.splice(0,e.length,...i)}return n}function M(e,t,n){switch(e){case`$set`:return re(t,n);case`$unset`:return ie(t);case`$push`:return A(t,n);case`$pull`:return j(t,n)}}function N(e,t,n){if(t.kind!==`property`){if(e===`$unset`)return O(`UNSUPPORTED_TARGET`,e,n,`$unset addresses object properties; '${n}' addresses a collection entry.`,`Remove a collection entry with $pull instead of $unset.`);if(e===`$push`||e===`$pull`)return O(`UNSUPPORTED_TARGET`,e,n,`${e} addresses a collection; '${n}' addresses one of its entries.`,`Point ${e} at the collection itself, without the trailing entry selector.`)}}function P(e,t,n,r){let i=c(e,ee(r));if(i===void 0)return O(`PATH_NOT_DECLARED`,t,n,`The artifact kind does not declare '${n}'.`,`Correct the path to one the kind schema declares; a misspelled field is never created.`);if((t===`$push`||t===`$pull`)&&!te(i))return O(`TARGET_NOT_A_COLLECTION`,t,n,`The artifact kind declares '${n}' as something other than a collection.`,`Use $set to replace '${n}', or point ${t} at a declared collection.`)}function F(e,t,n,r,i){let{operator:o,path:s,value:c}=t,l=a(s),u=l.at(-1);if(u===void 0)return k(o,s,`'${s}' addresses nothing.`);let d=N(o,u,s);if(d)return d;let f=P(r,o,s,l),p=i.allowUndeclaredRemovals===!0&&f?.code===`PATH_NOT_DECLARED`&&o===`$unset`;if(f&&!p)return f;let m=D(e,l.slice(0,-1),n);if(!m.ok){let e=ne(m.segment);return m.segment.kind===`property`?O(`PATH_NOT_RESOLVABLE`,o,s,`This revision has no value at '${e}' along '${s}'.`,`Set the value at '${e}' before addressing anything below it.`):k(o,s,`'${e}' in '${s}' addressed no entry in this revision.`)}let h=o===`$unset`||o===`$pull`,g=w(m.containers,u,n,h);if(g.length===0)return k(o,s,`'${s}' addressed no entry in this revision.`);if(u.kind===`index`&&g.length<m.containers.length)return k(o,s,`'${s}' addressed no entry in every selected collection of this revision.`);let _=M(o,g,c);return _===void 0?O(`TARGET_NOT_A_COLLECTION`,o,s,`The value at '${s}' is not a collection in this revision.`,`Use $set to replace '${s}', or point ${o} at a collection.`):_===0?k(o,s,`'${s}' changed nothing in this revision.`):{operator:o,path:s,matched:_}}function I(e,t,n,r={}){let a=structuredClone(e),o=x(t),s=[];for(let e of i(t)){let t=F(a,e,o,n,r);if(`code`in t)return{ok:!1,error:t};s.push(t)}return{ok:!0,application:{data:a,operations:s}}}function L(e){return`conflictingRevision`in e}function R(e){return e instanceof Error?e.message:String(e)}function z(e){return{ok:!1,error:e}}function B(e,t,n){return{refClass:`artifact`,kind:e,id:t,revision:n}}function V(e){return{path:e.path,reason:e.reason,...e.expectedType===void 0?{}:{expectedType:e.expectedType},...e.allowedValues===void 0?{}:{allowedValues:[...e.allowedValues]}}}function H(e){return e.representations!==void 0||e.schemaVersion!==void 0?!1:i(e.patch).every(({operator:e,path:t})=>e===`$push`&&a(t).every(e=>e.kind===`property`))}function U(e,t){return{code:`BASE_REVISION_CONFLICT`,message:`Artifact '${e.ref.kind}:${e.ref.id}' has advanced to revision '${t}'.`,currentRevision:t,repair:H(e)?`Resend the same patch with baseRevision '${t}'; it only appends at a fixed path, with no position and no filter, so it does not depend on the payload you read.`:`Re-read the artifact at revision '${t}' and rewrite the patch: only an append at a fixed path survives a concurrent write, with no position and no filter. Replacing, removing, addressing an entry by position, appending through a $[filter] placeholder, replacing or clearing representations, and naming a target schemaVersion all depend on the payload you read, which the concurrent revision may have changed.`}}function W(e){let t=e[0];if(!t)return`Correct the patched result so it satisfies the kind schema.`;let n=t.path===``?`the payload root`:`'${t.path}'`;return t.allowedValues?`${n} accepts one of: ${t.allowedValues.map(e=>typeof e==`string`?e:JSON.stringify(e)).join(`, `)}.`:t.expectedType?`${n} expects type ${t.expectedType}.`:`${n} ${t.reason}.`}function G(e,t){try{f(e,t.titlePath);return}catch(e){return{code:`SCHEMA_VALIDATION_FAILED`,message:`The patched result does not satisfy the '${t.kind}' data schema.`,issues:[{path:t.titlePath,reason:R(e)}],repair:`'${t.titlePath}' must be a nonblank string.`}}}function K(e){try{return o(e)}catch(t){return{code:`HOST_FAILED`,message:`Artifact kind '${e.kind}' could not compile its data schema: ${R(t)}`,repair:`Correct the registered data schema before patching artifacts of this kind.`}}}function q(e,t,n){let r=e.filter(e=>e.kind===t.kind);if(r.length===0)return{code:`KIND_NOT_REGISTERED`,message:`Artifact kind '${t.kind}' is not registered.`,repair:`Register the kind, or address an artifact of a registered kind.`};let i=r.find(e=>e.schemaVersion===n);if(!i){let e=[...new Set(r.map(e=>e.schemaVersion))].sort((e,t)=>e-t),i=e.join(`, `),a=n===t.schemaVersion?`Revision '${t.revision}' uses schema version ${n}`:`The request targets schema version ${n} for revision '${t.revision}' (schema version ${t.schemaVersion})`,o=e.filter(e=>e>t.schemaVersion).join(`, `);return{code:`SCHEMA_VERSION_MISMATCH`,message:`${a}, for which '${t.kind}' has no registration; registered: ${i}.`,repair:o===``?`Register '${t.kind}' at schema version ${n}${n>t.schemaVersion?``:` or newer`}; no registered version is newer than the revision's.`:`Set schemaVersion to one of ${o} and add the instructions that make the payload fit that version, or register '${t.kind}' at schema version ${n}.`}}return i}async function J(e,t,n){let r;try{r=await n.resolveCurrent({kind:e.ref.kind,id:e.ref.id},t)}catch(e){return{code:`HOST_FAILED`,message:`Artifact lookup failed: ${R(e)}`,repair:`Retry once the artifact store is reachable.`}}return r?r.kind!==e.ref.kind||r.id!==e.ref.id?{code:`HOST_FAILED`,message:`Artifact lookup returned a different artifact identity.`,repair:`Retry; the resolved artifact did not match the requested identity.`}:r.revision===e.baseRevision?r:U(e,r.revision):{code:`ARTIFACT_NOT_FOUND`,message:`Artifact '${e.ref.kind}:${e.ref.id}' was not found.`,repair:`Check the kind and identity, or create the artifact before revising it.`}}async function Y(e,t,n){let r=await J(e,t,n);if(`code`in r)return z(r);let i=B(r.kind,r.id,r.revision),a;try{a=await n.listKinds(e.ref.kind,t)}catch(e){return z({code:`HOST_FAILED`,message:`Artifact kind lookup failed: ${R(e)}`,repair:`Retry once the kind catalog is reachable.`})}let o=ae(e,r,a);if(`code`in o)return z(o);let s=o.schemaVersion!==r.schemaVersion,c=K(o);if(typeof c!=`function`)return z(c);let l=I(r.data,e.patch,o.dataSchema,{allowUndeclaredRemovals:s});if(!l.ok)return z(l.error);let u=c(l.application.data);if(!u.valid)return z({code:`SCHEMA_VALIDATION_FAILED`,message:`The patched result does not satisfy the '${o.kind}' data schema.`,issues:u.issues.map(V),repair:W(u.issues)});let d=G(l.application.data,o);if(d)return z(d);let f=[...l.application.operations],p=s?{migration:{from:r.schemaVersion,to:o.schemaVersion}}:{};if(e.dryRun===!0)return{ok:!0,base:i,dryRun:!0,operations:f,...p};let m;try{m=await n.store({previous:r,data:l.application.data,schemaVersion:o.schemaVersion,...e.statusPath===void 0?{}:{statusPath:e.statusPath},...e.representations===void 0?{}:{representations:e.representations}},t)}catch(e){return z({code:`HOST_FAILED`,message:`Artifact revision failed: ${R(e)}`,repair:`Re-read the artifact: the write may have been committed before the failure was reported. Retry only after confirming the change is absent.`})}if(L(m))return z(U(e,m.conflictingRevision));let h=oe(m,i,o.schemaVersion);return h?z(h):{ok:!0,base:i,dryRun:!1,artifact:B(m.kind,m.id,m.revision),operations:f,...p}}function ae(e,t,n){if(e.schemaVersion!==void 0&&e.schemaVersion<t.schemaVersion)return{code:`SCHEMA_VERSION_MISMATCH`,message:`The request targets schema version ${e.schemaVersion}, older than revision '${t.revision}' (schema version ${t.schemaVersion}); a migration never moves an artifact back.`,repair:`Omit schemaVersion to patch at version ${t.schemaVersion}, or name a newer registered version to migrate the artifact forward.`};let r=q(n,t,e.schemaVersion??t.schemaVersion);return`code`in r?r:r.schemaVersion===t.schemaVersion&&i(e.patch).length===0?{code:`NO_CHANGE`,message:`The patch carries no instruction and targets schema version ${r.schemaVersion}, which revision '${t.revision}' already has.`,repair:`Add at least one instruction, or name a different schemaVersion to migrate the artifact.`}:r}function oe(e,t,n){if(e.kind!==t.kind||e.id!==t.id||e.revision===t.revision)return{code:`HOST_FAILED`,message:`The store returned an artifact that is not a new revision of the patched one.`,repair:`Re-read the artifact before patching it again; the write outcome is unclear.`};if(e.schemaVersion!==n)return{code:`HOST_FAILED`,message:`The store persisted revision '${e.revision}' at schema version ${e.schemaVersion} instead of ${n}.`,repair:`Re-read the artifact; the host did not store the revision at the schema version the patch was validated against.`}}async function X(e,t,n){return n?v(await Y(e,t,n)):_(m.PERMISSION_DENIED,`Artifact revisions require an authorized host.`)}function Z(e){return h({name:`artifacts_patch`,description:`Revise an Artifact by sending only the change. Name the artifact, the baseRevision you read, and the instructions: $set and $unset on declared paths, $push and $pull on declared collections. Address one collection entry by field match with $[name] plus arrayFilters, or by position. An unknown path, an unknown operator and addressing nothing are all errors, never silent no-ops. Use dryRun to check a patch without writing. A stale baseRevision reports the current revision; follow the repair field of that error: only an append at a fixed path (no position, no filter) may be resent with the new baseRevision, anything else needs a fresh read and a rewritten patch. Rendering hints are separate from data: omit representations to keep them, send an object to replace all of them (no merge), or null to clear them. Name schemaVersion to migrate an artifact left at an older version by a kind bump: the patched result is validated against that version's registration and stored at it; omit it to keep the base revision's version. A migration may $unset a property the target no longer declares, and may carry no instruction when the payload already fits.`,annotations:{readOnly:!1,idempotent:!1},inputSchema:t,outputSchema:n,execute:(t,n)=>X(t,n,e)})}function Q(e){return g({name:`artifact-patch`,description:`Revise Artifacts by patch through an authorized host.`,version:`0.1.0`,tools:[y(Z(e))]})}function $(e){return{name:`artifact-patch`,displayName:`Artifact Patch Tools`,version:`0.1.0`,surface:`headless`,tools:{createToolsets:()=>e?[Q(e)]:[]}}}const se=$();export{X as a,Z as i,$ as n,Y as o,Q as r,I as s,se as t};
|