@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 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 `store` that throws leaves the outcome unknown — the contract covers the compare-and-swap,
61
- not what a throw means — so the repair hint asks for a re-read instead of promising a safe retry.
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. The package never issues raw Artifact bus requests and
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-DQhu5uYZ.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};
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};
@@ -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
- /** Either the persisted revision, or the conflict that stopped it. */
48
- export type ArtifactPatchStoreResult = ArtifactRevision | ArtifactPatchStoreConflict;
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. Refusing a write by returning
87
- * `ArtifactPatchStoreConflict` is the only outcome that promises nothing was
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-DQhu5uYZ.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};
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-1789138982585",
4
+ "version": "1.0.0-dev-1789148673705",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
7
7
  "repository": {
@@ -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};