@beyondwork/docx-react-component 1.0.120 → 1.0.122
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 +1 -0
- package/dist/api/public-types.cjs +1713 -55
- package/dist/api/public-types.d.cts +2 -2
- package/dist/api/public-types.d.ts +2 -2
- package/dist/api/public-types.js +6 -6
- package/dist/api/v3.cjs +4958 -406
- package/dist/api/v3.d.cts +3 -3
- package/dist/api/v3.d.ts +3 -3
- package/dist/api/v3.js +14 -14
- package/dist/{canonical-document-fNawStsc.d.cts → canonical-document-ByIqTd4s.d.cts} +9 -1
- package/dist/{canonical-document-fNawStsc.d.ts → canonical-document-ByIqTd4s.d.ts} +9 -1
- package/dist/{chunk-5RNMPLXU.js → chunk-37SEJQ3G.js} +4 -4
- package/dist/{chunk-FXGQM2JB.js → chunk-3OFSP2IX.js} +11 -5
- package/dist/{chunk-U5BSQXF4.js → chunk-3OHVK2D6.js} +70 -12
- package/dist/{chunk-AUQDC5BD.js → chunk-3TUQCHYT.js} +101 -2
- package/dist/{chunk-SJSMYEMU.js → chunk-B4YHWFE3.js} +3 -3
- package/dist/{chunk-XC56YLIS.js → chunk-C2LWJ4CZ.js} +4 -0
- package/dist/{chunk-VDIUVT46.js → chunk-CX42VC67.js} +1 -1
- package/dist/{chunk-KCHEAX4Z.js → chunk-EMDH4IQN.js} +148 -70
- package/dist/{chunk-TMQGWF7R.js → chunk-G3B2OBCZ.js} +352 -17
- package/dist/{chunk-VCL5MJMZ.js → chunk-GON2DNTE.js} +149 -28
- package/dist/{chunk-WVZX4NYG.js → chunk-GZW2ERUO.js} +601 -47
- package/dist/{chunk-WDNEPRFW.js → chunk-ICX54W4U.js} +1 -1
- package/dist/{chunk-FIGWJ43K.js → chunk-IT2DK3A7.js} +1883 -90
- package/dist/{chunk-2ZWFQ74R.js → chunk-OBCP6VTG.js} +1 -1
- package/dist/{chunk-FLNQY74K.js → chunk-OYGMRRR7.js} +1 -1
- package/dist/{chunk-MPYYBRVN.js → chunk-PCXTMEGY.js} +782 -124
- package/dist/{chunk-4JNUMMM7.js → chunk-PGGPPZ65.js} +17 -2
- package/dist/{chunk-KHZNNBTN.js → chunk-QFU7ZOAD.js} +43 -39
- package/dist/{chunk-4ZNQFWFM.js → chunk-QIO6V46H.js} +84 -4
- package/dist/{chunk-IQ2VJEF6.js → chunk-QNGJRZ2D.js} +1 -1
- package/dist/{chunk-BM5NSDII.js → chunk-S4ANOS2M.js} +2 -2
- package/dist/{chunk-AQA7OZ2R.js → chunk-TFSXOIAI.js} +959 -43
- package/dist/{chunk-NQZUGMLW.js → chunk-TMU7JMXX.js} +184 -32
- package/dist/{chunk-KD5K5XIA.js → chunk-UHQOUTAX.js} +568 -88
- package/dist/{chunk-327AIUXL.js → chunk-UWDWGQH5.js} +11 -4
- package/dist/{chunk-BBB4GSDB.js → chunk-XVFENXLK.js} +2 -2
- package/dist/{chunk-MUEN2WOG.js → chunk-ZKSDVHGH.js} +6 -3
- package/dist/compare.cjs +17 -2
- package/dist/compare.d.cts +1 -1
- package/dist/compare.d.ts +1 -1
- package/dist/compare.js +3 -3
- package/dist/core/commands/formatting-commands.d.cts +2 -2
- package/dist/core/commands/formatting-commands.d.ts +2 -2
- package/dist/core/commands/image-commands.cjs +814 -45
- package/dist/core/commands/image-commands.d.cts +2 -2
- package/dist/core/commands/image-commands.d.ts +2 -2
- package/dist/core/commands/image-commands.js +8 -8
- package/dist/core/commands/section-layout-commands.d.cts +2 -2
- package/dist/core/commands/section-layout-commands.d.ts +2 -2
- package/dist/core/commands/style-commands.d.cts +2 -2
- package/dist/core/commands/style-commands.d.ts +2 -2
- package/dist/core/commands/table-structure-commands.cjs +750 -42
- package/dist/core/commands/table-structure-commands.d.cts +2 -2
- package/dist/core/commands/table-structure-commands.d.ts +2 -2
- package/dist/core/commands/table-structure-commands.js +6 -6
- package/dist/core/commands/text-commands.cjs +910 -57
- package/dist/core/commands/text-commands.d.cts +2 -2
- package/dist/core/commands/text-commands.d.ts +2 -2
- package/dist/core/commands/text-commands.js +8 -8
- package/dist/core/selection/mapping.d.cts +2 -2
- package/dist/core/selection/mapping.d.ts +2 -2
- package/dist/core/state/editor-state.cjs +17 -2
- package/dist/core/state/editor-state.d.cts +2 -2
- package/dist/core/state/editor-state.d.ts +2 -2
- package/dist/core/state/editor-state.js +2 -2
- package/dist/index.cjs +6203 -625
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +299 -67
- package/dist/io/docx-session.cjs +354 -102
- package/dist/io/docx-session.d.cts +4 -4
- package/dist/io/docx-session.d.ts +4 -4
- package/dist/io/docx-session.js +5 -5
- package/dist/legal.cjs +183 -31
- package/dist/legal.d.cts +1 -1
- package/dist/legal.d.ts +1 -1
- package/dist/legal.js +3 -3
- package/dist/{loader-CaohrhNl.d.ts → loader-BF8ju_LK.d.ts} +22 -4
- package/dist/{loader-BpCyGnZl.d.cts → loader-g54WRvj1.d.cts} +22 -4
- package/dist/{public-types-Dpch9JG0.d.cts → public-types-D_y4Ptcj.d.cts} +747 -21
- package/dist/{public-types-C948HNVF.d.ts → public-types-Dl1jiWjk.d.ts} +747 -21
- package/dist/public-types.cjs +1713 -55
- package/dist/public-types.d.cts +2 -2
- package/dist/public-types.d.ts +2 -2
- package/dist/public-types.js +6 -6
- package/dist/runtime/collab.cjs +4 -0
- package/dist/runtime/collab.d.cts +3 -3
- package/dist/runtime/collab.d.ts +3 -3
- package/dist/runtime/collab.js +2 -2
- package/dist/runtime/document-runtime.cjs +3699 -507
- package/dist/runtime/document-runtime.d.cts +2 -2
- package/dist/runtime/document-runtime.d.ts +2 -2
- package/dist/runtime/document-runtime.js +18 -18
- package/dist/{session-Dqg17V3s.d.ts → session-C1EPAkcI.d.ts} +3 -3
- package/dist/{session-BlXE5zxz.d.cts → session-D15QOO0Q.d.cts} +3 -3
- package/dist/session.cjs +951 -104
- package/dist/session.d.cts +5 -5
- package/dist/session.d.ts +5 -5
- package/dist/session.js +10 -8
- package/dist/tailwind.cjs +1752 -91
- package/dist/tailwind.d.cts +2 -2
- package/dist/tailwind.d.ts +2 -2
- package/dist/tailwind.js +10 -10
- package/dist/{types-C9vZVpKy.d.cts → types-BoSRp2Vg.d.cts} +2 -2
- package/dist/{types-B1tlF1bq.d.ts → types-DEvRwq9C.d.ts} +2 -2
- package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +3 -3
- package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +3 -3
- package/dist/ui-tailwind/editor-surface/search-plugin.js +7 -7
- package/dist/ui-tailwind/theme/editor-theme.css +5 -5
- package/dist/ui-tailwind.cjs +1752 -91
- package/dist/ui-tailwind.d.cts +8 -8
- package/dist/ui-tailwind.d.ts +8 -8
- package/dist/ui-tailwind.js +10 -10
- package/package.json +17 -5
- package/dist/ui-tailwind/theme/tokens.css +0 -382
|
@@ -17,18 +17,18 @@ import {
|
|
|
17
17
|
resolveScopeRange,
|
|
18
18
|
scopeSpecificity,
|
|
19
19
|
searchDocument
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-PCXTMEGY.js";
|
|
21
21
|
import {
|
|
22
22
|
BROADCAST_COMMAND_TYPES,
|
|
23
23
|
COMMAND_EVENT_SCHEMA_VERSION,
|
|
24
24
|
LOCAL_ONLY_COMMAND_TYPES,
|
|
25
25
|
getRemoteCursorStates
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-C2LWJ4CZ.js";
|
|
27
27
|
import {
|
|
28
28
|
buildPageAnchorElementId,
|
|
29
29
|
createUiApi,
|
|
30
30
|
emitUxResponse
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-GON2DNTE.js";
|
|
32
32
|
import {
|
|
33
33
|
DEFAULT_PX_PER_TWIP,
|
|
34
34
|
DEFAULT_REGISTRY_ENTRIES,
|
|
@@ -36,23 +36,23 @@ import {
|
|
|
36
36
|
LAYCACHE_SCHEMA_VERSION,
|
|
37
37
|
LAYOUT_ENGINE_VERSION,
|
|
38
38
|
createScopeTagRegistry
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-G3B2OBCZ.js";
|
|
40
40
|
import {
|
|
41
41
|
chartModelStore,
|
|
42
42
|
createFormattingContext
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-3TUQCHYT.js";
|
|
44
44
|
import {
|
|
45
45
|
deriveDocumentStats
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-OYGMRRR7.js";
|
|
47
47
|
import {
|
|
48
48
|
DocxSession
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-S4ANOS2M.js";
|
|
50
50
|
import {
|
|
51
51
|
WORKFLOW_PAYLOAD_PART_PATH,
|
|
52
52
|
buildEditorStateXml,
|
|
53
53
|
parseEditorStateXml,
|
|
54
54
|
parseWorkflowPayloadEnvelopeFromPackage
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-EMDH4IQN.js";
|
|
56
56
|
import {
|
|
57
57
|
EMU_PER_PX,
|
|
58
58
|
GRADIENT_STOP_UNITS,
|
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
createHeaderFooterStoryKey,
|
|
62
62
|
createNoteStoryKey,
|
|
63
63
|
validateEditableTargetRef
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-TFSXOIAI.js";
|
|
65
65
|
import {
|
|
66
66
|
readOpcPackage
|
|
67
67
|
} from "./chunk-OL2UEHRP.js";
|
|
@@ -1894,6 +1894,7 @@ function toV3PageLocalStory(story, frame) {
|
|
|
1894
1894
|
variant: story.variant,
|
|
1895
1895
|
relationshipId: story.relationshipId,
|
|
1896
1896
|
...story.sectionIndex !== void 0 ? { sectionIndex: story.sectionIndex } : {},
|
|
1897
|
+
previewText: story.previewText,
|
|
1897
1898
|
resolvedFields: story.resolvedFields.map(toV3PageLocalStoryField),
|
|
1898
1899
|
anchoredObjects: story.anchoredObjects.map(toV3PageLocalStoryObject),
|
|
1899
1900
|
measuredFrameHeightTwips: story.measuredFrameHeightTwips,
|
|
@@ -3819,19 +3820,24 @@ function getRuntimeTableSelectionDescriptor(snapshot) {
|
|
|
3819
3820
|
const tableBlockIndex = tableDepth > 1 ? -1 : anchor.tableBlockIndex;
|
|
3820
3821
|
const spansMultipleCells = anchor.rowIndex !== head.rowIndex || anchor.columnIndex !== head.columnIndex || !snapshot.selection.isCollapsed;
|
|
3821
3822
|
const hasEditableTableTarget = target !== void 0 && tableBlockPath !== void 0;
|
|
3823
|
+
const nonEditableReason = anchor.nonEditableReason ?? head.nonEditableReason;
|
|
3824
|
+
const editability = nonEditableReason !== void 0 || tableDepth > 1 && !hasEditableTableTarget ? "non-editable" : "editable";
|
|
3822
3825
|
const descriptor = {
|
|
3823
|
-
editability
|
|
3824
|
-
...
|
|
3826
|
+
editability,
|
|
3827
|
+
...nonEditableReason !== void 0 ? {
|
|
3828
|
+
nonEditableReason,
|
|
3829
|
+
message: nonEditableReason === "vertical-merge-continuation-not-editable" ? "Vertical-merge continuation cells are synthetic layout cells; edit the visible restart cell instead." : "Nested table selection descriptors require a validated editable target path."
|
|
3830
|
+
} : tableDepth > 1 && !hasEditableTableTarget ? {
|
|
3825
3831
|
nonEditableReason: "nested-table-not-editable",
|
|
3826
3832
|
message: "Nested table selection descriptors require a validated editable target path."
|
|
3827
3833
|
} : {},
|
|
3828
3834
|
tableDepth,
|
|
3829
|
-
...target ? { storyKey: target.storyKey } : {},
|
|
3835
|
+
...editability === "editable" && target ? { storyKey: target.storyKey } : {},
|
|
3830
3836
|
...tableBlockPath ? {
|
|
3831
3837
|
tableBlockPath,
|
|
3832
|
-
tableKey: `${target?.storyKey ?? "unknown"}:${tableBlockPath}`
|
|
3838
|
+
tableKey: `${editability === "editable" ? target?.storyKey ?? "unknown" : "unknown"}:${tableBlockPath}`
|
|
3833
3839
|
} : {},
|
|
3834
|
-
...target ? {
|
|
3840
|
+
...editability === "editable" && target ? {
|
|
3835
3841
|
editableTarget: {
|
|
3836
3842
|
targetKey: target.targetKey,
|
|
3837
3843
|
kind: target.kind,
|
|
@@ -3884,7 +3890,8 @@ function findCellHit(blocks, position, tableDepth) {
|
|
|
3884
3890
|
columnIndex: currentColumn,
|
|
3885
3891
|
colspan: Math.max(1, cell.colspan),
|
|
3886
3892
|
rowspan: Math.max(1, cell.rowspan),
|
|
3887
|
-
...editableTarget ? { editableTarget } : {}
|
|
3893
|
+
...editableTarget && validTableEditableTarget(editableTarget) ? { editableTarget } : {},
|
|
3894
|
+
...cell.verticalMerge === "continue" || editableTarget?.posture.blockers.includes("synthetic-layout-cell") ? { nonEditableReason: "vertical-merge-continuation-not-editable" } : {}
|
|
3888
3895
|
};
|
|
3889
3896
|
}
|
|
3890
3897
|
}
|
|
@@ -4015,21 +4022,6 @@ function copyEnvelope(envelope) {
|
|
|
4015
4022
|
...envelope.sourceIdentity ? { sourceIdentity: { ...envelope.sourceIdentity } } : {}
|
|
4016
4023
|
};
|
|
4017
4024
|
}
|
|
4018
|
-
var UNAVAILABLE_COVERAGE = {
|
|
4019
|
-
status: "unavailable",
|
|
4020
|
-
pageCount: 0,
|
|
4021
|
-
pageMaterialization: { paginated: 0, unpaginated: 0 },
|
|
4022
|
-
regionCount: 0,
|
|
4023
|
-
sliceCount: 0,
|
|
4024
|
-
lineCount: 0,
|
|
4025
|
-
anchorCount: 0,
|
|
4026
|
-
hitTargetCount: 0,
|
|
4027
|
-
semanticEntryCount: 0,
|
|
4028
|
-
editableTargetCount: 0,
|
|
4029
|
-
replacementEnvelopeCount: 0,
|
|
4030
|
-
objectHandleCount: 0,
|
|
4031
|
-
precision: { exact: 0, "within-tolerance": 0, heuristic: 0 }
|
|
4032
|
-
};
|
|
4033
4025
|
function copyLayoutDivergence(divergence) {
|
|
4034
4026
|
return {
|
|
4035
4027
|
divergenceId: divergence.divergenceId,
|
|
@@ -4056,16 +4048,6 @@ function copyGraphOracleReference(reference) {
|
|
|
4056
4048
|
};
|
|
4057
4049
|
}
|
|
4058
4050
|
function projectLayoutEvidence(runtime, pageIds) {
|
|
4059
|
-
if (typeof runtime.layout?.getPageCount !== "function") {
|
|
4060
|
-
return {
|
|
4061
|
-
status: "unavailable",
|
|
4062
|
-
pageCount: 0,
|
|
4063
|
-
pageIds: [],
|
|
4064
|
-
pagesWithDivergences: [],
|
|
4065
|
-
divergenceIds: [],
|
|
4066
|
-
divergences: []
|
|
4067
|
-
};
|
|
4068
|
-
}
|
|
4069
4051
|
const pageCount = runtime.layout.getPageCount();
|
|
4070
4052
|
const selectedPageIds = pageIds ? new Set(pageIds) : null;
|
|
4071
4053
|
const pageIdOut = [];
|
|
@@ -4199,26 +4181,14 @@ function projectOracleEvidence(runtime, options, input) {
|
|
|
4199
4181
|
}
|
|
4200
4182
|
function projectDocumentPe2Evidence(runtime, options) {
|
|
4201
4183
|
const layout = projectLayoutEvidence(runtime);
|
|
4202
|
-
const geometry = runtime.geometry ? copyCoverage(runtime.geometry.getGeometryCoverage()) : UNAVAILABLE_COVERAGE;
|
|
4203
4184
|
return {
|
|
4204
|
-
geometry,
|
|
4185
|
+
geometry: copyCoverage(runtime.geometry.getGeometryCoverage()),
|
|
4205
4186
|
layout,
|
|
4206
4187
|
oracle: projectOracleEvidence(runtime, options, { pageIds: layout.pageIds })
|
|
4207
4188
|
};
|
|
4208
4189
|
}
|
|
4209
4190
|
function projectScopePe2Evidence(runtime, handle, options) {
|
|
4210
4191
|
const { scopeId } = handle;
|
|
4211
|
-
if (!runtime.geometry) {
|
|
4212
|
-
const layout2 = projectLayoutEvidence(runtime, []);
|
|
4213
|
-
return {
|
|
4214
|
-
geometry: {
|
|
4215
|
-
coverage: UNAVAILABLE_COVERAGE,
|
|
4216
|
-
reason: "geometry-index-unavailable"
|
|
4217
|
-
},
|
|
4218
|
-
layout: layout2,
|
|
4219
|
-
oracle: projectOracleEvidence(runtime, options, { scopeId, pageIds: layout2.pageIds })
|
|
4220
|
-
};
|
|
4221
|
-
}
|
|
4222
4192
|
const coverage = copyCoverage(runtime.geometry.getGeometryCoverage());
|
|
4223
4193
|
const index = runtime.geometry.getGeometryIndex();
|
|
4224
4194
|
if (!index) {
|
|
@@ -4651,17 +4621,19 @@ function capabilityBlockerDetailFor(code) {
|
|
|
4651
4621
|
}
|
|
4652
4622
|
function readbackBlockerDetailFor(code) {
|
|
4653
4623
|
const unchangedPrefix = "apply-readback-unchanged:";
|
|
4624
|
+
const mismatchPrefix = "apply-readback-mismatch:";
|
|
4654
4625
|
const unresolvablePrefix = "apply-readback-unresolvable:";
|
|
4655
4626
|
const isUnchanged = code.startsWith(unchangedPrefix);
|
|
4627
|
+
const isMismatch = code.startsWith(mismatchPrefix);
|
|
4656
4628
|
const isUnresolvable = code.startsWith(unresolvablePrefix);
|
|
4657
|
-
if (!isUnchanged && !isUnresolvable) return null;
|
|
4658
|
-
const scopeId = isUnchanged ? code.slice(unchangedPrefix.length) : code.slice(unresolvablePrefix.length);
|
|
4629
|
+
if (!isUnchanged && !isMismatch && !isUnresolvable) return null;
|
|
4630
|
+
const scopeId = isUnchanged ? code.slice(unchangedPrefix.length) : isMismatch ? code.slice(mismatchPrefix.length) : code.slice(unresolvablePrefix.length);
|
|
4659
4631
|
return {
|
|
4660
4632
|
code,
|
|
4661
4633
|
category: "capability-prerequisite",
|
|
4662
4634
|
scopeKind: "scope",
|
|
4663
|
-
message: isUnchanged ? `Runtime dispatch completed, but authoritative readback showed target scope ${scopeId} content was unchanged.` : `Runtime dispatch completed, but target scope ${scopeId} could not be re-resolved for authoritative readback.`,
|
|
4664
|
-
nextStep: isUnchanged ? "Treat this as a hard refusal for content mutation. Attach an explanation or issue, and wait for an L08 mutation/readback route before surfacing warn-and-proceed." : "Re-resolve the target with ai.resolveReference or ai.queryScopeAtPosition, then retry only with the returned handle's scopeId; otherwise attach an explanation or issue."
|
|
4635
|
+
message: isUnchanged ? `Runtime dispatch completed, but authoritative readback showed target scope ${scopeId} content was unchanged.` : isMismatch ? `Runtime dispatch completed, but authoritative readback did not match the requested replacement for target scope ${scopeId}.` : `Runtime dispatch completed, but target scope ${scopeId} could not be re-resolved for authoritative readback.`,
|
|
4636
|
+
nextStep: isUnchanged ? "Treat this as a hard refusal for content mutation. Attach an explanation or issue, and wait for an L08 mutation/readback route before surfacing warn-and-proceed." : isMismatch ? "Treat this as a hard refusal for content mutation. Re-resolve the target and retry only after the replacement path has a proven mutation/readback route." : "Re-resolve the target with ai.resolveReference or ai.queryScopeAtPosition, then retry only with the returned handle's scopeId; otherwise attach an explanation or issue."
|
|
4665
4637
|
};
|
|
4666
4638
|
}
|
|
4667
4639
|
function projectBlockerDetails(blockers) {
|
|
@@ -4722,6 +4694,15 @@ function createReplacementFamily(runtime) {
|
|
|
4722
4694
|
warnings: Object.freeze([TEXTBOX_TARGET_AVAILABLE_WARNING])
|
|
4723
4695
|
};
|
|
4724
4696
|
}
|
|
4697
|
+
const generatedOrLinkedBlockers = generatedOrLinkedReplacementBlockers(bundle, operation);
|
|
4698
|
+
if (generatedOrLinkedBlockers.length > 0) {
|
|
4699
|
+
return {
|
|
4700
|
+
proposalId,
|
|
4701
|
+
safe: false,
|
|
4702
|
+
posture: "hard-refusal",
|
|
4703
|
+
blockers: generatedOrLinkedBlockers
|
|
4704
|
+
};
|
|
4705
|
+
}
|
|
4725
4706
|
const proposedContent = typeof input.proposedText === "string" ? { kind: "text", text: input.proposedText } : { kind: "text", text: "" };
|
|
4726
4707
|
const verdict = composeScopeValidation({
|
|
4727
4708
|
scope: compiled.scope,
|
|
@@ -4831,6 +4812,15 @@ function createReplacementFamily(runtime) {
|
|
|
4831
4812
|
}
|
|
4832
4813
|
};
|
|
4833
4814
|
}
|
|
4815
|
+
function generatedOrLinkedReplacementBlockers(bundle, operation) {
|
|
4816
|
+
if (!bundle || operation !== "replace") return Object.freeze([]);
|
|
4817
|
+
const blockers = bundle.evidence.capabilities?.canReplaceText.blockers ?? [];
|
|
4818
|
+
return Object.freeze(
|
|
4819
|
+
blockers.filter(
|
|
4820
|
+
(blocker2) => blocker2 === "field-generated-text" || blocker2.startsWith("link-bookmark:")
|
|
4821
|
+
)
|
|
4822
|
+
);
|
|
4823
|
+
}
|
|
4834
4824
|
|
|
4835
4825
|
// src/api/v3/ai/_metadata-audit.ts
|
|
4836
4826
|
function snapshotDocumentHash(runtime) {
|
|
@@ -5658,7 +5648,7 @@ function createTableActionFamily(runtime) {
|
|
|
5658
5648
|
);
|
|
5659
5649
|
const surface = runtime.getRenderSnapshot().surface?.blocks ?? [];
|
|
5660
5650
|
const currentTargets = collectEditableTargetRefs(runtime.getCanonicalDocument()).filter(
|
|
5661
|
-
(target) => target.table &&
|
|
5651
|
+
(target) => target.table && supportedTableKeys.has(target.table.tableKey) && isResolvableCurrentTableActionTarget(runtime, surface, target)
|
|
5662
5652
|
);
|
|
5663
5653
|
const seed = documentSeed(runtime);
|
|
5664
5654
|
const byActionHandle = /* @__PURE__ */ new Map();
|
|
@@ -5710,18 +5700,19 @@ function createTableActionFamily(runtime) {
|
|
|
5710
5700
|
if (warning) {
|
|
5711
5701
|
return blockedResult(input, proposalId, warning);
|
|
5712
5702
|
}
|
|
5713
|
-
const targetBlocker = validateTableTarget(target, input.operation
|
|
5703
|
+
const targetBlocker = validateTableTarget(target, input.operation);
|
|
5714
5704
|
if (targetBlocker) {
|
|
5715
5705
|
return blockedResult(input, proposalId, targetBlocker);
|
|
5716
5706
|
}
|
|
5717
5707
|
if (isTableTextOperation(input.operation)) {
|
|
5718
5708
|
const textContent = tableTextContent(input.operation);
|
|
5719
|
-
|
|
5709
|
+
const fragmentContent = tableStructuredFragment(input.operation);
|
|
5710
|
+
if (textContent === null && fragmentContent === null) {
|
|
5720
5711
|
return blockedResult(input, proposalId, {
|
|
5721
5712
|
code: `table-action-text-content-required:${input.operation.kind}`,
|
|
5722
5713
|
category: "unsupported-operation",
|
|
5723
|
-
message: "Paste/drop table text actions require plain text
|
|
5724
|
-
nextStep: 'Retry with operation.content.kind="text" and a string text value
|
|
5714
|
+
message: "Paste/drop table text actions require plain text or a canonical document fragment.",
|
|
5715
|
+
nextStep: 'Retry with operation.content.kind="text" and a string text value, or operation.content.kind="structured" with a CanonicalDocumentFragment payload.',
|
|
5725
5716
|
actionHandle: input.actionHandle,
|
|
5726
5717
|
operation: input.operation.kind,
|
|
5727
5718
|
operationScope: target.table?.operationScope
|
|
@@ -5745,12 +5736,21 @@ function createTableActionFamily(runtime) {
|
|
|
5745
5736
|
});
|
|
5746
5737
|
}
|
|
5747
5738
|
const before2 = runtime.getCanonicalDocument();
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5739
|
+
if (fragmentContent) {
|
|
5740
|
+
runtime.dispatch({
|
|
5741
|
+
type: "fragment.insert",
|
|
5742
|
+
fragment: fragmentContent,
|
|
5743
|
+
editableTarget: target,
|
|
5744
|
+
origin: { source: "api", timestamp: nowUtc }
|
|
5745
|
+
});
|
|
5746
|
+
} else {
|
|
5747
|
+
runtime.dispatch({
|
|
5748
|
+
type: "table.apply-text",
|
|
5749
|
+
operation: { type: "replace-text", text: textContent ?? "" },
|
|
5750
|
+
editableTarget: target,
|
|
5751
|
+
origin: { source: "api", timestamp: nowUtc }
|
|
5752
|
+
});
|
|
5753
|
+
}
|
|
5754
5754
|
const changed2 = runtime.getCanonicalDocument() !== before2;
|
|
5755
5755
|
const afterReadback = tableTextReadback(readEditableTargetText(runtime.getCanonicalDocument(), target));
|
|
5756
5756
|
return {
|
|
@@ -5760,7 +5760,7 @@ function createTableActionFamily(runtime) {
|
|
|
5760
5760
|
actionHandle: input.actionHandle,
|
|
5761
5761
|
operation: input.operation.kind,
|
|
5762
5762
|
commandReference: {
|
|
5763
|
-
command: "table.apply-text",
|
|
5763
|
+
command: fragmentContent ? "fragment.insert" : "table.apply-text",
|
|
5764
5764
|
actorId: input.actorId ?? "v3-ai-api",
|
|
5765
5765
|
origin: input.origin ?? "agent",
|
|
5766
5766
|
emittedAtUtc: nowUtc
|
|
@@ -5775,14 +5775,15 @@ function createTableActionFamily(runtime) {
|
|
|
5775
5775
|
}
|
|
5776
5776
|
const resolution = resolveEditableTableStructureTarget({
|
|
5777
5777
|
document: runtime.getCanonicalDocument(),
|
|
5778
|
-
target
|
|
5778
|
+
target,
|
|
5779
|
+
selectionDescriptor: operationSelectionDescriptor(input.operation)
|
|
5779
5780
|
});
|
|
5780
5781
|
if (resolution.kind === "rejected") {
|
|
5781
5782
|
return blockedResult(input, proposalId, {
|
|
5782
5783
|
code: resolution.blockedReason.code,
|
|
5783
5784
|
category: "blocked-target",
|
|
5784
5785
|
message: resolution.blockedReason.message,
|
|
5785
|
-
nextStep: "Use a supported
|
|
5786
|
+
nextStep: "Use a supported table-structure target from ai.listTableActions; nested, SDT, stale, wrong-story, or workflow-blocked targets remain refused.",
|
|
5786
5787
|
actionHandle: input.actionHandle,
|
|
5787
5788
|
operation: input.operation.kind,
|
|
5788
5789
|
operationScope: target.table?.operationScope
|
|
@@ -5831,10 +5832,10 @@ function isSupportedTableActionEvidence(entry) {
|
|
|
5831
5832
|
return isSupportedTableStructureEvidence(entry) || isSupportedTableTextEvidence(entry);
|
|
5832
5833
|
}
|
|
5833
5834
|
function isSupportedTableStructureEvidence(entry) {
|
|
5834
|
-
return entry.commandFamily === "table-structure" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("table-structure-action") && Boolean(entry.table) && entry.
|
|
5835
|
+
return entry.commandFamily === "table-structure" && entry.runtimeCommand.status === "supported" && entry.runtimeCommand.intents.includes("table-structure-action") && Boolean(entry.table) && entry.table?.operationScope !== "text";
|
|
5835
5836
|
}
|
|
5836
5837
|
function isSupportedTableTextEvidence(entry) {
|
|
5837
|
-
return entry.commandFamily === "text-leaf" && entry.runtimeCommand.status === "supported" && Boolean(entry.table) && entry.
|
|
5838
|
+
return entry.commandFamily === "text-leaf" && entry.runtimeCommand.status === "supported" && Boolean(entry.table) && entry.table?.operationScope === "text";
|
|
5838
5839
|
}
|
|
5839
5840
|
function projectTableActionDescriptor(document, seed, entry) {
|
|
5840
5841
|
if (!isSupportedTableActionEvidence(entry) || !entry.table) {
|
|
@@ -5876,7 +5877,8 @@ function projectCurrentTableTarget(document, seed, target) {
|
|
|
5876
5877
|
reason: table.operationScope === "text" ? "l07:table-text-target-supported" : "l07:table-structure-target-supported"
|
|
5877
5878
|
};
|
|
5878
5879
|
}
|
|
5879
|
-
function validateTableTarget(target,
|
|
5880
|
+
function validateTableTarget(target, operationInput) {
|
|
5881
|
+
const operation = operationInput.kind;
|
|
5880
5882
|
if (operation === "paste-cell-content" || operation === "drop-cell-content") {
|
|
5881
5883
|
if (target.commandFamily !== "text-leaf" || target.table?.operationScope !== "text") {
|
|
5882
5884
|
return {
|
|
@@ -5912,6 +5914,9 @@ function validateTableTarget(target, operation) {
|
|
|
5912
5914
|
}
|
|
5913
5915
|
const supported = operationsForScope(target.table.operationScope);
|
|
5914
5916
|
if (!supported.includes(operation)) {
|
|
5917
|
+
if (operation === "merge-cells" && target.table.operationScope === "cell" && operationSelectionDescriptor(operationInput) !== null) {
|
|
5918
|
+
return null;
|
|
5919
|
+
}
|
|
5915
5920
|
const callable = callableOperationsForScope(target.table.operationScope);
|
|
5916
5921
|
if (callable.includes(operation)) return null;
|
|
5917
5922
|
return {
|
|
@@ -5926,18 +5931,7 @@ function validateTableTarget(target, operation) {
|
|
|
5926
5931
|
return null;
|
|
5927
5932
|
}
|
|
5928
5933
|
function tableActionWarning(input, target) {
|
|
5929
|
-
if (
|
|
5930
|
-
return {
|
|
5931
|
-
code: `table-action-unsupported:structured-cell-content:${input.operation.kind}`,
|
|
5932
|
-
category: "unsupported-operation",
|
|
5933
|
-
message: "Structured paste/drop payloads are not accepted for command-native table text actions.",
|
|
5934
|
-
nextStep: "Retry with plain text content, or use a product paste path that can produce a validated fragment command.",
|
|
5935
|
-
actionHandle: input.actionHandle,
|
|
5936
|
-
operation: input.operation.kind,
|
|
5937
|
-
operationScope: target.table?.operationScope
|
|
5938
|
-
};
|
|
5939
|
-
}
|
|
5940
|
-
if (input.operation.kind === "merge-cells" && target.table?.operationScope === "cell") {
|
|
5934
|
+
if (input.operation.kind === "merge-cells" && target.table?.operationScope === "cell" && operationSelectionDescriptor(input.operation) === null) {
|
|
5941
5935
|
return {
|
|
5942
5936
|
code: "table-action-unsupported:merge-cells:single-cell-target",
|
|
5943
5937
|
category: "unsupported-operation",
|
|
@@ -5955,7 +5949,7 @@ function callableOperationsForScope(scope) {
|
|
|
5955
5949
|
case "text":
|
|
5956
5950
|
return TEXT_TRANSFER_OPERATIONS;
|
|
5957
5951
|
case "cell":
|
|
5958
|
-
return CELL_STRUCTURE_OPERATIONS;
|
|
5952
|
+
return [...CELL_STRUCTURE_OPERATIONS, "merge-cells"];
|
|
5959
5953
|
default:
|
|
5960
5954
|
return operationsForScope(scope);
|
|
5961
5955
|
}
|
|
@@ -5980,7 +5974,7 @@ function resolveTableActionHandle(runtime, seed, actionHandle) {
|
|
|
5980
5974
|
const document = runtime.getCanonicalDocument();
|
|
5981
5975
|
const surface = runtime.getRenderSnapshot().surface?.blocks ?? [];
|
|
5982
5976
|
const targets = collectEditableTargetRefs(document).filter(
|
|
5983
|
-
(target) => target.table &&
|
|
5977
|
+
(target) => target.table && callableOperationsForScope(target.table.operationScope).length > 0 && isResolvableCurrentTableActionTarget(runtime, surface, target)
|
|
5984
5978
|
);
|
|
5985
5979
|
return targets.find((target) => {
|
|
5986
5980
|
return tableActionHandle(seed, target.targetKey) === actionHandle;
|
|
@@ -6013,7 +6007,7 @@ function hashOpaque(input) {
|
|
|
6013
6007
|
return (h >>> 0).toString(36).padStart(7, "0");
|
|
6014
6008
|
}
|
|
6015
6009
|
function toTableStructureOperation(operation) {
|
|
6016
|
-
const { kind, ...rest } = operation;
|
|
6010
|
+
const { kind, selectionDescriptor: _selectionDescriptor, ...rest } = operation;
|
|
6017
6011
|
return {
|
|
6018
6012
|
...rest,
|
|
6019
6013
|
type: kind
|
|
@@ -6025,6 +6019,19 @@ function isTableTextOperation(operation) {
|
|
|
6025
6019
|
function tableTextContent(operation) {
|
|
6026
6020
|
return operation.content?.kind === "text" ? operation.content.text : null;
|
|
6027
6021
|
}
|
|
6022
|
+
function tableStructuredFragment(operation) {
|
|
6023
|
+
if (operation.content?.kind !== "structured") return null;
|
|
6024
|
+
return isCanonicalDocumentFragment(operation.content.value) ? operation.content.value : null;
|
|
6025
|
+
}
|
|
6026
|
+
function isCanonicalDocumentFragment(value) {
|
|
6027
|
+
return Boolean(
|
|
6028
|
+
value && typeof value === "object" && Array.isArray(value.blocks)
|
|
6029
|
+
);
|
|
6030
|
+
}
|
|
6031
|
+
function operationSelectionDescriptor(operation) {
|
|
6032
|
+
const descriptor = operation.selectionDescriptor;
|
|
6033
|
+
return descriptor ?? null;
|
|
6034
|
+
}
|
|
6028
6035
|
function tableActionTargetReadback(actionHandle, target) {
|
|
6029
6036
|
return {
|
|
6030
6037
|
actionHandle,
|
|
@@ -6132,6 +6139,1791 @@ function documentSeed(runtime) {
|
|
|
6132
6139
|
return state.documentId ?? "document";
|
|
6133
6140
|
}
|
|
6134
6141
|
|
|
6142
|
+
// src/api/v3/ai/actions.ts
|
|
6143
|
+
function actionMethodMetadata(method, readOrMutate, auditCategory, contextPromptShape, uxIntent) {
|
|
6144
|
+
return {
|
|
6145
|
+
name: `ai.actions.${method}`,
|
|
6146
|
+
status: "live-with-adapter",
|
|
6147
|
+
sourceLayer: "ai",
|
|
6148
|
+
liveEvidence: {
|
|
6149
|
+
runnerTest: "test/api/v3/ai/ai-actions-category.test.ts",
|
|
6150
|
+
commit: "refactor-09-ai-actions-category"
|
|
6151
|
+
},
|
|
6152
|
+
uxIntent,
|
|
6153
|
+
agentMetadata: {
|
|
6154
|
+
readOrMutate,
|
|
6155
|
+
boundedScope: "scope",
|
|
6156
|
+
auditCategory,
|
|
6157
|
+
contextPromptShape
|
|
6158
|
+
},
|
|
6159
|
+
stateClass: "A-canonical",
|
|
6160
|
+
persistsTo: "canonical",
|
|
6161
|
+
...readOrMutate === "mutate" ? { broadcastsVia: "crdt" } : {},
|
|
6162
|
+
rwdReference: `\xA7AI API \xA7 ai.actions.${method}`
|
|
6163
|
+
};
|
|
6164
|
+
}
|
|
6165
|
+
var discoverMetadata = actionMethodMetadata(
|
|
6166
|
+
"discover",
|
|
6167
|
+
"read",
|
|
6168
|
+
"actions-discover",
|
|
6169
|
+
"Read supported simple action methods, policy vocabulary, and optional target capability posture.",
|
|
6170
|
+
{ uiVisible: false, expectsUxResponse: "none" }
|
|
6171
|
+
);
|
|
6172
|
+
var locateMetadata = actionMethodMetadata(
|
|
6173
|
+
"locate",
|
|
6174
|
+
"read",
|
|
6175
|
+
"actions-locate",
|
|
6176
|
+
"Find the first scope/table-text target by query; returns ScopeHandle or opaque table actionHandle, never offsets.",
|
|
6177
|
+
{ uiVisible: false, expectsUxResponse: "none" }
|
|
6178
|
+
);
|
|
6179
|
+
var locateAllMetadata = actionMethodMetadata(
|
|
6180
|
+
"locateAll",
|
|
6181
|
+
"read",
|
|
6182
|
+
"actions-locate",
|
|
6183
|
+
"Find scope/table-text targets by query; table text matches include readback {text,isEmpty}.",
|
|
6184
|
+
{ uiVisible: false, expectsUxResponse: "none" }
|
|
6185
|
+
);
|
|
6186
|
+
var rewriteMetadata = actionMethodMetadata(
|
|
6187
|
+
"rewrite",
|
|
6188
|
+
"mutate",
|
|
6189
|
+
"actions-rewrite",
|
|
6190
|
+
"Rewrite one target through applyReplacementScope or table actionHandle/table.apply-text.",
|
|
6191
|
+
{
|
|
6192
|
+
uiVisible: true,
|
|
6193
|
+
expectsUxResponse: "inline-change",
|
|
6194
|
+
expectedDelta: "target text changes"
|
|
6195
|
+
}
|
|
6196
|
+
);
|
|
6197
|
+
var rewriteAllMetadata = actionMethodMetadata(
|
|
6198
|
+
"rewriteAll",
|
|
6199
|
+
"mutate",
|
|
6200
|
+
"actions-rewrite",
|
|
6201
|
+
"Rewrite every locateAll match up to the supplied limit through the same scope/table text routes as rewrite.",
|
|
6202
|
+
{
|
|
6203
|
+
uiVisible: true,
|
|
6204
|
+
expectsUxResponse: "inline-change",
|
|
6205
|
+
expectedDelta: "matched target text changes"
|
|
6206
|
+
}
|
|
6207
|
+
);
|
|
6208
|
+
var insertTextMetadata = actionMethodMetadata(
|
|
6209
|
+
"insertText",
|
|
6210
|
+
"mutate",
|
|
6211
|
+
"actions-insert-text",
|
|
6212
|
+
"Insert text before or after a semantic scope through the scoped replacement path.",
|
|
6213
|
+
{
|
|
6214
|
+
uiVisible: true,
|
|
6215
|
+
expectsUxResponse: "inline-change",
|
|
6216
|
+
expectedDelta: "adjacent text is inserted"
|
|
6217
|
+
}
|
|
6218
|
+
);
|
|
6219
|
+
var flagMetadata = actionMethodMetadata(
|
|
6220
|
+
"flag",
|
|
6221
|
+
"mutate",
|
|
6222
|
+
"actions-flag",
|
|
6223
|
+
"Create one durable AI issue flag on a semantic scope.",
|
|
6224
|
+
{
|
|
6225
|
+
uiVisible: true,
|
|
6226
|
+
expectsUxResponse: "warning-added",
|
|
6227
|
+
expectedDelta: "issue rail shows a new issue card"
|
|
6228
|
+
}
|
|
6229
|
+
);
|
|
6230
|
+
var flagManyMetadata = actionMethodMetadata(
|
|
6231
|
+
"flagMany",
|
|
6232
|
+
"mutate",
|
|
6233
|
+
"actions-flag",
|
|
6234
|
+
"Create durable AI issue flags on locateAll scope matches.",
|
|
6235
|
+
{
|
|
6236
|
+
uiVisible: true,
|
|
6237
|
+
expectsUxResponse: "warning-added",
|
|
6238
|
+
expectedDelta: "issue rail shows new issue cards"
|
|
6239
|
+
}
|
|
6240
|
+
);
|
|
6241
|
+
var markMetadata = actionMethodMetadata(
|
|
6242
|
+
"mark",
|
|
6243
|
+
"mutate",
|
|
6244
|
+
"actions-mark",
|
|
6245
|
+
"Apply a scoped formatting action through applyScopeAction.",
|
|
6246
|
+
{
|
|
6247
|
+
uiVisible: true,
|
|
6248
|
+
expectsUxResponse: "inline-change",
|
|
6249
|
+
expectedDelta: "target formatting changes"
|
|
6250
|
+
}
|
|
6251
|
+
);
|
|
6252
|
+
var briefMetadata = actionMethodMetadata(
|
|
6253
|
+
"brief",
|
|
6254
|
+
"read",
|
|
6255
|
+
"actions-brief",
|
|
6256
|
+
"Read a compact document or target summary without invoking a model provider.",
|
|
6257
|
+
{ uiVisible: false, expectsUxResponse: "none" }
|
|
6258
|
+
);
|
|
6259
|
+
var fieldRefreshMetadata = actionMethodMetadata(
|
|
6260
|
+
"fieldRefresh",
|
|
6261
|
+
"mutate",
|
|
6262
|
+
"actions-generated-content",
|
|
6263
|
+
"Refresh a supported generated field target through the modeled runtime field command.",
|
|
6264
|
+
{
|
|
6265
|
+
uiVisible: true,
|
|
6266
|
+
expectsUxResponse: "inline-change",
|
|
6267
|
+
expectedDelta: "generated field result refreshes"
|
|
6268
|
+
}
|
|
6269
|
+
);
|
|
6270
|
+
var tocRefreshMetadata = actionMethodMetadata(
|
|
6271
|
+
"tocRefresh",
|
|
6272
|
+
"mutate",
|
|
6273
|
+
"actions-generated-content",
|
|
6274
|
+
"Refresh a supported table-of-contents target through the modeled runtime TOC command.",
|
|
6275
|
+
{
|
|
6276
|
+
uiVisible: true,
|
|
6277
|
+
expectsUxResponse: "inline-change",
|
|
6278
|
+
expectedDelta: "table-of-contents result refreshes"
|
|
6279
|
+
}
|
|
6280
|
+
);
|
|
6281
|
+
var bookmarkEditMetadata = actionMethodMetadata(
|
|
6282
|
+
"bookmarkEdit",
|
|
6283
|
+
"mutate",
|
|
6284
|
+
"actions-link-bookmark",
|
|
6285
|
+
"Edit a supported bookmark content range through the modeled runtime bookmark command.",
|
|
6286
|
+
{
|
|
6287
|
+
uiVisible: true,
|
|
6288
|
+
expectsUxResponse: "inline-change",
|
|
6289
|
+
expectedDelta: "bookmark content changes"
|
|
6290
|
+
}
|
|
6291
|
+
);
|
|
6292
|
+
var hyperlinkDestinationEditMetadata = actionMethodMetadata(
|
|
6293
|
+
"hyperlinkDestinationEdit",
|
|
6294
|
+
"mutate",
|
|
6295
|
+
"actions-link-bookmark",
|
|
6296
|
+
"Update a supported hyperlink destination through the modeled runtime hyperlink command.",
|
|
6297
|
+
{
|
|
6298
|
+
uiVisible: true,
|
|
6299
|
+
expectsUxResponse: "inline-change",
|
|
6300
|
+
expectedDelta: "hyperlink destination changes"
|
|
6301
|
+
}
|
|
6302
|
+
);
|
|
6303
|
+
var listOperationMetadata = actionMethodMetadata(
|
|
6304
|
+
"listOperation",
|
|
6305
|
+
"mutate",
|
|
6306
|
+
"actions-list-operation",
|
|
6307
|
+
"Apply list toggle, indent, outdent, restart, or continue-numbering commands to paragraph-like scope handles.",
|
|
6308
|
+
{
|
|
6309
|
+
uiVisible: true,
|
|
6310
|
+
expectsUxResponse: "inline-change",
|
|
6311
|
+
expectedDelta: "list structure changes"
|
|
6312
|
+
}
|
|
6313
|
+
);
|
|
6314
|
+
var tableFragmentMetadata = actionMethodMetadata(
|
|
6315
|
+
"tableFragment",
|
|
6316
|
+
"mutate",
|
|
6317
|
+
"actions-table-fragment",
|
|
6318
|
+
"Apply structured table paste/drop through an opaque table actionHandle.",
|
|
6319
|
+
{
|
|
6320
|
+
uiVisible: true,
|
|
6321
|
+
expectsUxResponse: "inline-change",
|
|
6322
|
+
expectedDelta: "table cell fragment content changes"
|
|
6323
|
+
}
|
|
6324
|
+
);
|
|
6325
|
+
var tableSelectionMetadata = actionMethodMetadata(
|
|
6326
|
+
"tableSelection",
|
|
6327
|
+
"mutate",
|
|
6328
|
+
"actions-table-selection",
|
|
6329
|
+
"Apply table merge/split selection operations through an opaque table actionHandle.",
|
|
6330
|
+
{
|
|
6331
|
+
uiVisible: true,
|
|
6332
|
+
expectsUxResponse: "inline-change",
|
|
6333
|
+
expectedDelta: "table cell selection structure changes"
|
|
6334
|
+
}
|
|
6335
|
+
);
|
|
6336
|
+
var runPlanMetadata = actionMethodMetadata(
|
|
6337
|
+
"runPlan",
|
|
6338
|
+
"mutate",
|
|
6339
|
+
"actions-run-plan",
|
|
6340
|
+
"Validate or apply a host-generated structured plan by dispatching exact steps through existing action primitives; no free-form instruction interpretation.",
|
|
6341
|
+
{
|
|
6342
|
+
uiVisible: true,
|
|
6343
|
+
expectsUxResponse: "inline-change",
|
|
6344
|
+
expectedDelta: "planned step mutations are applied when mode is apply"
|
|
6345
|
+
}
|
|
6346
|
+
);
|
|
6347
|
+
var ACTION_METHODS = Object.freeze([
|
|
6348
|
+
"discover",
|
|
6349
|
+
"locate",
|
|
6350
|
+
"locateAll",
|
|
6351
|
+
"rewrite",
|
|
6352
|
+
"rewriteAll",
|
|
6353
|
+
"insertText",
|
|
6354
|
+
"flag",
|
|
6355
|
+
"flagMany",
|
|
6356
|
+
"mark",
|
|
6357
|
+
"brief",
|
|
6358
|
+
"fieldRefresh",
|
|
6359
|
+
"tocRefresh",
|
|
6360
|
+
"bookmarkEdit",
|
|
6361
|
+
"hyperlinkDestinationEdit",
|
|
6362
|
+
"listOperation",
|
|
6363
|
+
"tableFragment",
|
|
6364
|
+
"tableSelection",
|
|
6365
|
+
"runPlan"
|
|
6366
|
+
]);
|
|
6367
|
+
var DEFAULT_LOCATE_LIMIT = 20;
|
|
6368
|
+
var DEFAULT_REWRITE_ALL_LIMIT = 10;
|
|
6369
|
+
var DEFAULT_TABLE_TEXT_SCOPE_LIMIT = 3;
|
|
6370
|
+
var DEFAULT_PLAN_STEP_LIMIT = 20;
|
|
6371
|
+
function createActionsFamily(runtime) {
|
|
6372
|
+
const category = {
|
|
6373
|
+
discover(input) {
|
|
6374
|
+
const policies = createPolicyFamily(runtime).listAIActions();
|
|
6375
|
+
if (!input?.target) {
|
|
6376
|
+
return {
|
|
6377
|
+
status: "supported",
|
|
6378
|
+
methods: ACTION_METHODS,
|
|
6379
|
+
policyActions: policies
|
|
6380
|
+
};
|
|
6381
|
+
}
|
|
6382
|
+
const resolved = resolveTarget(runtime, input.target);
|
|
6383
|
+
if (!resolved.ok) {
|
|
6384
|
+
return {
|
|
6385
|
+
status: "blocked",
|
|
6386
|
+
methods: ACTION_METHODS,
|
|
6387
|
+
policyActions: policies,
|
|
6388
|
+
blockers: resolved.blockers,
|
|
6389
|
+
blockerDetails: resolved.blockerDetails
|
|
6390
|
+
};
|
|
6391
|
+
}
|
|
6392
|
+
return {
|
|
6393
|
+
status: "supported",
|
|
6394
|
+
methods: ACTION_METHODS,
|
|
6395
|
+
policyActions: policies,
|
|
6396
|
+
target: summarizeTarget(resolved.target)
|
|
6397
|
+
};
|
|
6398
|
+
},
|
|
6399
|
+
locate(input) {
|
|
6400
|
+
const all = locateAll(runtime, { ...input, limit: input.limit ?? 1 });
|
|
6401
|
+
return {
|
|
6402
|
+
status: all.status,
|
|
6403
|
+
matches: all.matches.slice(0, 1),
|
|
6404
|
+
...all.blockers ? { blockers: all.blockers } : {},
|
|
6405
|
+
...all.blockerDetails ? { blockerDetails: all.blockerDetails } : {}
|
|
6406
|
+
};
|
|
6407
|
+
},
|
|
6408
|
+
locateAll(input) {
|
|
6409
|
+
return locateAll(runtime, input);
|
|
6410
|
+
},
|
|
6411
|
+
rewrite(input) {
|
|
6412
|
+
if (input.text === void 0) {
|
|
6413
|
+
return blockedApply(
|
|
6414
|
+
"actions:rewrite:text-required",
|
|
6415
|
+
"input",
|
|
6416
|
+
"Rewrite actions require a text value.",
|
|
6417
|
+
"Retry with a text string."
|
|
6418
|
+
);
|
|
6419
|
+
}
|
|
6420
|
+
const resolved = resolveTarget(runtime, input.target);
|
|
6421
|
+
if (!resolved.ok) return blockedApplyFromResolution(resolved);
|
|
6422
|
+
return applyRewrite(runtime, resolved.target, input);
|
|
6423
|
+
},
|
|
6424
|
+
rewriteAll(input) {
|
|
6425
|
+
const located = locateAll(runtime, {
|
|
6426
|
+
query: input.query,
|
|
6427
|
+
kind: input.kind,
|
|
6428
|
+
matchCase: input.matchCase,
|
|
6429
|
+
limit: input.limit ?? DEFAULT_REWRITE_ALL_LIMIT,
|
|
6430
|
+
includeTableText: input.includeTableText,
|
|
6431
|
+
tableTextScopeLimit: input.tableTextScopeLimit
|
|
6432
|
+
});
|
|
6433
|
+
if (located.status !== "found") {
|
|
6434
|
+
return blockedApply(
|
|
6435
|
+
located.blockers?.[0] ?? `actions:locate:not-found:${input.query}`,
|
|
6436
|
+
located.status === "blocked" ? "input" : "unresolved-target",
|
|
6437
|
+
located.status === "blocked" ? "The locate request was blocked." : "No scope or table text action matched the query.",
|
|
6438
|
+
"Call ai.actions.locateAll with a broader query, then retry with a returned handle or actionHandle.",
|
|
6439
|
+
located.blockerDetails
|
|
6440
|
+
);
|
|
6441
|
+
}
|
|
6442
|
+
const results = located.matches.map((match) => {
|
|
6443
|
+
const target = match.actionHandle ? { actionHandle: match.actionHandle } : match.handle ? { handle: match.handle } : null;
|
|
6444
|
+
if (!target) {
|
|
6445
|
+
return blockedApply(
|
|
6446
|
+
"actions:rewrite-all:match-without-target",
|
|
6447
|
+
"blocked",
|
|
6448
|
+
"A locate match did not carry a callable target.",
|
|
6449
|
+
"Refresh the locate result and retry."
|
|
6450
|
+
);
|
|
6451
|
+
}
|
|
6452
|
+
const resolved = resolveTarget(runtime, target);
|
|
6453
|
+
if (!resolved.ok) return blockedApplyFromResolution(resolved);
|
|
6454
|
+
return applyRewrite(runtime, resolved.target, input);
|
|
6455
|
+
});
|
|
6456
|
+
const applied = results.filter((result) => result.applied);
|
|
6457
|
+
return {
|
|
6458
|
+
status: applied.length === results.length ? "applied" : applied.length > 0 ? "partial" : "blocked",
|
|
6459
|
+
applied: applied.length > 0,
|
|
6460
|
+
changed: results.some((result) => result.changed),
|
|
6461
|
+
results: Object.freeze(results)
|
|
6462
|
+
};
|
|
6463
|
+
},
|
|
6464
|
+
insertText(input) {
|
|
6465
|
+
const resolved = resolveTarget(runtime, input.target);
|
|
6466
|
+
if (!resolved.ok) return blockedApplyFromResolution(resolved);
|
|
6467
|
+
if (resolved.target.kind !== "scope") {
|
|
6468
|
+
return blockedApply(
|
|
6469
|
+
"actions:insert-text:table-text-adjacent-unsupported",
|
|
6470
|
+
"unsupported",
|
|
6471
|
+
"Adjacent text insertion is not supported for table text action handles.",
|
|
6472
|
+
"Use ai.actions.rewrite with a table text actionHandle when replacing the cell text is acceptable."
|
|
6473
|
+
);
|
|
6474
|
+
}
|
|
6475
|
+
const result = createReplacementFamily(runtime).applyReplacementScope({
|
|
6476
|
+
targetScopeId: resolved.target.handle.scopeId,
|
|
6477
|
+
operation: input.position === "before" ? "insert-before" : "insert-after",
|
|
6478
|
+
proposedText: input.text,
|
|
6479
|
+
...input.actionId ? { actionId: input.actionId } : {},
|
|
6480
|
+
...input.reason ? { reason: input.reason } : {},
|
|
6481
|
+
...input.preserve ? { preserve: input.preserve } : {},
|
|
6482
|
+
...input.actorId ? { actorId: input.actorId } : {},
|
|
6483
|
+
...input.origin ? { origin: input.origin } : {},
|
|
6484
|
+
...input.proposalId ? { proposalId: input.proposalId } : {}
|
|
6485
|
+
});
|
|
6486
|
+
return projectApplyResult(result, resolved.target);
|
|
6487
|
+
},
|
|
6488
|
+
flag(input) {
|
|
6489
|
+
const resolved = resolveTarget(runtime, input.target);
|
|
6490
|
+
if (!resolved.ok) return blockedApplyFromResolution(resolved);
|
|
6491
|
+
if (resolved.target.kind !== "scope") {
|
|
6492
|
+
return blockedApply(
|
|
6493
|
+
"actions:flag:table-text-handle-unsupported",
|
|
6494
|
+
"unsupported",
|
|
6495
|
+
"Issue flags attach to semantic scopes, not table text action handles.",
|
|
6496
|
+
"Use the owning table or paragraph scope handle when creating an issue flag."
|
|
6497
|
+
);
|
|
6498
|
+
}
|
|
6499
|
+
const result = createAttachFamily(runtime).createIssue({
|
|
6500
|
+
scopeId: resolved.target.handle.scopeId,
|
|
6501
|
+
summary: input.summary,
|
|
6502
|
+
...input.severity ? { severity: input.severity } : {},
|
|
6503
|
+
...input.actorId ? { actorId: input.actorId } : {},
|
|
6504
|
+
...input.origin ? { origin: input.origin } : {}
|
|
6505
|
+
});
|
|
6506
|
+
return {
|
|
6507
|
+
status: result.created ? "applied" : "blocked",
|
|
6508
|
+
applied: result.created,
|
|
6509
|
+
changed: result.created,
|
|
6510
|
+
target: summarizeTarget(resolved.target),
|
|
6511
|
+
proposalId: result.issueId,
|
|
6512
|
+
...result.reason ? { blockers: Object.freeze([result.reason]) } : {},
|
|
6513
|
+
...result.reason ? {
|
|
6514
|
+
blockerDetails: Object.freeze([
|
|
6515
|
+
blocker(
|
|
6516
|
+
result.reason,
|
|
6517
|
+
"unresolved-target",
|
|
6518
|
+
"The issue flag target could not be resolved.",
|
|
6519
|
+
"Refresh the scope handle and retry."
|
|
6520
|
+
)
|
|
6521
|
+
])
|
|
6522
|
+
} : {},
|
|
6523
|
+
...result.auditReference ? { auditReference: result.auditReference } : {}
|
|
6524
|
+
};
|
|
6525
|
+
},
|
|
6526
|
+
flagMany(input) {
|
|
6527
|
+
const located = locateAll(runtime, input);
|
|
6528
|
+
if (located.status !== "found") {
|
|
6529
|
+
return blockedApply(
|
|
6530
|
+
located.blockers?.[0] ?? `actions:locate:not-found:${input.query}`,
|
|
6531
|
+
located.status === "blocked" ? "input" : "unresolved-target",
|
|
6532
|
+
"No scope matched the flag query.",
|
|
6533
|
+
"Call ai.actions.locateAll with a broader query, then retry with a returned handle.",
|
|
6534
|
+
located.blockerDetails
|
|
6535
|
+
);
|
|
6536
|
+
}
|
|
6537
|
+
const results = located.matches.map((match) => {
|
|
6538
|
+
if (!match.handle) {
|
|
6539
|
+
return blockedApply(
|
|
6540
|
+
"actions:flag-many:table-text-handle-unsupported",
|
|
6541
|
+
"unsupported",
|
|
6542
|
+
"Issue flags attach to semantic scopes, not table text action handles.",
|
|
6543
|
+
"Use a semantic scope handle for issue flags."
|
|
6544
|
+
);
|
|
6545
|
+
}
|
|
6546
|
+
const resolved = resolveTarget(runtime, { handle: match.handle });
|
|
6547
|
+
if (!resolved.ok) return blockedApplyFromResolution(resolved);
|
|
6548
|
+
const result = createAttachFamily(runtime).createIssue({
|
|
6549
|
+
scopeId: match.handle.scopeId,
|
|
6550
|
+
summary: input.summary,
|
|
6551
|
+
...input.severity ? { severity: input.severity } : {},
|
|
6552
|
+
...input.actorId ? { actorId: input.actorId } : {},
|
|
6553
|
+
...input.origin ? { origin: input.origin } : {}
|
|
6554
|
+
});
|
|
6555
|
+
return {
|
|
6556
|
+
status: result.created ? "applied" : "blocked",
|
|
6557
|
+
applied: result.created,
|
|
6558
|
+
changed: result.created,
|
|
6559
|
+
target: summarizeTarget(resolved.target),
|
|
6560
|
+
proposalId: result.issueId,
|
|
6561
|
+
...result.auditReference ? { auditReference: result.auditReference } : {}
|
|
6562
|
+
};
|
|
6563
|
+
});
|
|
6564
|
+
const applied = results.filter((result) => result.applied);
|
|
6565
|
+
return {
|
|
6566
|
+
status: applied.length === results.length ? "applied" : applied.length > 0 ? "partial" : "blocked",
|
|
6567
|
+
applied: applied.length > 0,
|
|
6568
|
+
changed: results.some((result) => result.changed),
|
|
6569
|
+
results: Object.freeze(results)
|
|
6570
|
+
};
|
|
6571
|
+
},
|
|
6572
|
+
mark(input) {
|
|
6573
|
+
const resolved = resolveTarget(runtime, input.target);
|
|
6574
|
+
if (!resolved.ok) return blockedApplyFromResolution(resolved);
|
|
6575
|
+
if (resolved.target.kind !== "scope") {
|
|
6576
|
+
return blockedApply(
|
|
6577
|
+
"actions:mark:table-text-handle-unsupported",
|
|
6578
|
+
"unsupported",
|
|
6579
|
+
"Mark actions apply to semantic scopes, not table text action handles.",
|
|
6580
|
+
"Use a semantic scope handle for formatting marks."
|
|
6581
|
+
);
|
|
6582
|
+
}
|
|
6583
|
+
const result = createReplacementFamily(runtime).applyScopeAction({
|
|
6584
|
+
targetScopeId: resolved.target.handle.scopeId,
|
|
6585
|
+
action: input.action,
|
|
6586
|
+
...input.actionId ? { actionId: input.actionId } : {},
|
|
6587
|
+
...input.reason ? { reason: input.reason } : {},
|
|
6588
|
+
...input.actorId ? { actorId: input.actorId } : {},
|
|
6589
|
+
...input.origin ? { origin: input.origin } : {},
|
|
6590
|
+
...input.proposalId ? { proposalId: input.proposalId } : {}
|
|
6591
|
+
});
|
|
6592
|
+
return projectApplyResult(result, resolved.target);
|
|
6593
|
+
},
|
|
6594
|
+
brief(input) {
|
|
6595
|
+
if (!input?.target) {
|
|
6596
|
+
return {
|
|
6597
|
+
status: "read",
|
|
6598
|
+
document: createInspectFamily(runtime).inspectDocument()
|
|
6599
|
+
};
|
|
6600
|
+
}
|
|
6601
|
+
const resolved = resolveTarget(runtime, input.target);
|
|
6602
|
+
if (!resolved.ok) {
|
|
6603
|
+
return {
|
|
6604
|
+
status: "blocked",
|
|
6605
|
+
blockers: resolved.blockers,
|
|
6606
|
+
blockerDetails: resolved.blockerDetails
|
|
6607
|
+
};
|
|
6608
|
+
}
|
|
6609
|
+
if (resolved.target.kind === "table-text") {
|
|
6610
|
+
const readback = resolved.target.action.readback;
|
|
6611
|
+
return {
|
|
6612
|
+
status: "read",
|
|
6613
|
+
target: summarizeTarget(resolved.target),
|
|
6614
|
+
...readback ? { text: readback.text, excerpt: excerpt(readback.text) } : {}
|
|
6615
|
+
};
|
|
6616
|
+
}
|
|
6617
|
+
const text = resolved.target.scope.content.text;
|
|
6618
|
+
return {
|
|
6619
|
+
status: "read",
|
|
6620
|
+
target: summarizeTarget(resolved.target),
|
|
6621
|
+
text,
|
|
6622
|
+
excerpt: resolved.target.scope.content.excerpt ?? excerpt(text)
|
|
6623
|
+
};
|
|
6624
|
+
},
|
|
6625
|
+
fieldRefresh(input) {
|
|
6626
|
+
return applyModeledCommand(
|
|
6627
|
+
runtime,
|
|
6628
|
+
input.target,
|
|
6629
|
+
"fieldRefresh",
|
|
6630
|
+
{
|
|
6631
|
+
type: "field.refresh",
|
|
6632
|
+
options: { supportedOnly: input.supportedOnly ?? true },
|
|
6633
|
+
origin: actionOrigin(runtime, input)
|
|
6634
|
+
},
|
|
6635
|
+
input
|
|
6636
|
+
);
|
|
6637
|
+
},
|
|
6638
|
+
tocRefresh(input) {
|
|
6639
|
+
return applyModeledCommand(
|
|
6640
|
+
runtime,
|
|
6641
|
+
input.target,
|
|
6642
|
+
"tocRefresh",
|
|
6643
|
+
{
|
|
6644
|
+
type: "toc.refresh",
|
|
6645
|
+
options: {
|
|
6646
|
+
...input.tocId ? { tocId: input.tocId } : {},
|
|
6647
|
+
...input.mode ? { mode: input.mode } : {},
|
|
6648
|
+
...input.maxLevel !== void 0 ? { maxLevel: input.maxLevel } : {}
|
|
6649
|
+
},
|
|
6650
|
+
origin: actionOrigin(runtime, input)
|
|
6651
|
+
},
|
|
6652
|
+
input
|
|
6653
|
+
);
|
|
6654
|
+
},
|
|
6655
|
+
bookmarkEdit(input) {
|
|
6656
|
+
if (input.text === void 0) {
|
|
6657
|
+
return blockedApply(
|
|
6658
|
+
"actions:bookmark-edit:text-required",
|
|
6659
|
+
"input",
|
|
6660
|
+
"Bookmark content edit actions require a text value.",
|
|
6661
|
+
"Retry with a text string."
|
|
6662
|
+
);
|
|
6663
|
+
}
|
|
6664
|
+
return applyModeledCommand(
|
|
6665
|
+
runtime,
|
|
6666
|
+
input.target,
|
|
6667
|
+
"bookmarkEdit",
|
|
6668
|
+
{
|
|
6669
|
+
type: "bookmark.edit-content",
|
|
6670
|
+
text: input.text,
|
|
6671
|
+
origin: actionOrigin(runtime, input)
|
|
6672
|
+
},
|
|
6673
|
+
input
|
|
6674
|
+
);
|
|
6675
|
+
},
|
|
6676
|
+
hyperlinkDestinationEdit(input) {
|
|
6677
|
+
if (!input.href) {
|
|
6678
|
+
return blockedApply(
|
|
6679
|
+
"actions:hyperlink-destination-edit:href-required",
|
|
6680
|
+
"input",
|
|
6681
|
+
"Hyperlink destination edit actions require a non-empty href.",
|
|
6682
|
+
"Retry with an href string."
|
|
6683
|
+
);
|
|
6684
|
+
}
|
|
6685
|
+
return applyModeledCommand(
|
|
6686
|
+
runtime,
|
|
6687
|
+
input.target,
|
|
6688
|
+
"hyperlinkDestinationEdit",
|
|
6689
|
+
{
|
|
6690
|
+
type: "hyperlink.update-destination",
|
|
6691
|
+
href: input.href,
|
|
6692
|
+
origin: actionOrigin(runtime, input)
|
|
6693
|
+
},
|
|
6694
|
+
input
|
|
6695
|
+
);
|
|
6696
|
+
},
|
|
6697
|
+
listOperation(input) {
|
|
6698
|
+
return applyListOperation(runtime, input);
|
|
6699
|
+
},
|
|
6700
|
+
tableFragment(input) {
|
|
6701
|
+
const result = createTableActionFamily(runtime).applyTableAction(input);
|
|
6702
|
+
return projectTableApplyResult(result);
|
|
6703
|
+
},
|
|
6704
|
+
tableSelection(input) {
|
|
6705
|
+
const result = createTableActionFamily(runtime).applyTableAction(input);
|
|
6706
|
+
return projectTableApplyResult(result);
|
|
6707
|
+
},
|
|
6708
|
+
runPlan(input) {
|
|
6709
|
+
return runPlan(runtime, input);
|
|
6710
|
+
}
|
|
6711
|
+
};
|
|
6712
|
+
const family = {
|
|
6713
|
+
actions() {
|
|
6714
|
+
return Object.freeze(category);
|
|
6715
|
+
}
|
|
6716
|
+
};
|
|
6717
|
+
return { actions: family.actions() };
|
|
6718
|
+
}
|
|
6719
|
+
function runPlan(runtime, input) {
|
|
6720
|
+
const mode = input.mode ?? "preview";
|
|
6721
|
+
const maxSteps = Math.max(0, input.maxSteps ?? DEFAULT_PLAN_STEP_LIMIT);
|
|
6722
|
+
if (!Array.isArray(input.steps) || input.steps.length === 0) {
|
|
6723
|
+
return blockedPlan(
|
|
6724
|
+
mode,
|
|
6725
|
+
"actions:plan:empty",
|
|
6726
|
+
"input",
|
|
6727
|
+
"Run plan requires at least one structured step.",
|
|
6728
|
+
"Have the host generate explicit steps with exact scope handles or opaque action handles."
|
|
6729
|
+
);
|
|
6730
|
+
}
|
|
6731
|
+
if (input.steps.length > maxSteps) {
|
|
6732
|
+
return blockedPlan(
|
|
6733
|
+
mode,
|
|
6734
|
+
`actions:plan:too-many-steps:${input.steps.length}`,
|
|
6735
|
+
"input",
|
|
6736
|
+
`Run plan received ${input.steps.length} steps, exceeding the limit of ${maxSteps}.`,
|
|
6737
|
+
"Split the host-generated plan into smaller calls and retry."
|
|
6738
|
+
);
|
|
6739
|
+
}
|
|
6740
|
+
const stopOnBlocker = input.stopOnBlocker ?? true;
|
|
6741
|
+
const results = [];
|
|
6742
|
+
for (const step of input.steps) {
|
|
6743
|
+
const result = runPlanStep(runtime, mode, step, input);
|
|
6744
|
+
results.push(result);
|
|
6745
|
+
if (stopOnBlocker && (result.status === "blocked" || result.status === "unsupported")) {
|
|
6746
|
+
break;
|
|
6747
|
+
}
|
|
6748
|
+
}
|
|
6749
|
+
return summarizePlanResult(mode, results);
|
|
6750
|
+
}
|
|
6751
|
+
function runPlanStep(runtime, mode, step, plan) {
|
|
6752
|
+
if (!step.id) {
|
|
6753
|
+
return blockedPlanStep(
|
|
6754
|
+
"unknown-step",
|
|
6755
|
+
step.kind,
|
|
6756
|
+
"actions:plan:step-id-required",
|
|
6757
|
+
"input",
|
|
6758
|
+
"Every plan step requires a stable id supplied by the host.",
|
|
6759
|
+
"Regenerate the plan with stable per-step ids."
|
|
6760
|
+
);
|
|
6761
|
+
}
|
|
6762
|
+
if (step.kind === "tableAction" || step.kind === "tableFragment" || step.kind === "tableSelection") {
|
|
6763
|
+
return runPlanTableActionStep(runtime, mode, step, plan);
|
|
6764
|
+
}
|
|
6765
|
+
const before = step.target ? readPlanTarget(runtime, step.target) : readDocumentPlanTarget(runtime);
|
|
6766
|
+
if (!before.ok) {
|
|
6767
|
+
return blockedPlanStepFromDetails(step.id, step.kind, before.blockerDetails);
|
|
6768
|
+
}
|
|
6769
|
+
const precondition = checkPlanPreconditions(step, before.readback);
|
|
6770
|
+
if (precondition) {
|
|
6771
|
+
return blockedPlanStepFromDetails(step.id, step.kind, [precondition], {
|
|
6772
|
+
target: before.target,
|
|
6773
|
+
beforeReadback: before.readback,
|
|
6774
|
+
tableAction: before.tableAction
|
|
6775
|
+
});
|
|
6776
|
+
}
|
|
6777
|
+
if (step.kind === "brief") {
|
|
6778
|
+
return {
|
|
6779
|
+
id: step.id,
|
|
6780
|
+
kind: step.kind,
|
|
6781
|
+
status: "read",
|
|
6782
|
+
applied: false,
|
|
6783
|
+
changed: false,
|
|
6784
|
+
...before.target ? { target: before.target } : {},
|
|
6785
|
+
...before.tableAction ? { tableAction: before.tableAction } : {},
|
|
6786
|
+
...before.readback ? { beforeReadback: before.readback } : {}
|
|
6787
|
+
};
|
|
6788
|
+
}
|
|
6789
|
+
const capabilityBlocker = checkPlanStepCapability(runtime, step, before);
|
|
6790
|
+
if (capabilityBlocker) {
|
|
6791
|
+
return blockedPlanStepFromDetails(step.id, step.kind, [capabilityBlocker], {
|
|
6792
|
+
target: before.target,
|
|
6793
|
+
beforeReadback: before.readback,
|
|
6794
|
+
tableAction: before.tableAction
|
|
6795
|
+
});
|
|
6796
|
+
}
|
|
6797
|
+
if (mode === "preview") {
|
|
6798
|
+
return {
|
|
6799
|
+
id: step.id,
|
|
6800
|
+
kind: step.kind,
|
|
6801
|
+
status: "planned",
|
|
6802
|
+
applied: false,
|
|
6803
|
+
changed: false,
|
|
6804
|
+
...before.target ? { target: before.target } : {},
|
|
6805
|
+
...before.tableAction ? { tableAction: before.tableAction } : {},
|
|
6806
|
+
...before.readback ? { beforeReadback: before.readback } : {}
|
|
6807
|
+
};
|
|
6808
|
+
}
|
|
6809
|
+
const applied = applyPlanStep(runtime, step, plan);
|
|
6810
|
+
const after = step.kind === "flag" ? before : step.target ? readPlanTarget(runtime, step.target) : before;
|
|
6811
|
+
return {
|
|
6812
|
+
id: step.id,
|
|
6813
|
+
kind: step.kind,
|
|
6814
|
+
status: applied.status === "unsupported" ? "unsupported" : applied.applied ? "applied" : "blocked",
|
|
6815
|
+
applied: applied.applied,
|
|
6816
|
+
changed: applied.changed,
|
|
6817
|
+
...applied.target ?? before.target ? { target: applied.target ?? before.target } : {},
|
|
6818
|
+
...before.tableAction ? { tableAction: before.tableAction } : {},
|
|
6819
|
+
...before.readback ? { beforeReadback: before.readback } : {},
|
|
6820
|
+
...after.ok && after.readback ? { afterReadback: after.readback } : {},
|
|
6821
|
+
...applied.proposalId ? { proposalId: applied.proposalId } : {},
|
|
6822
|
+
...applied.posture ? { posture: applied.posture } : {},
|
|
6823
|
+
...applied.blockers ? { blockers: applied.blockers } : {},
|
|
6824
|
+
...applied.blockerDetails ? { blockerDetails: applied.blockerDetails } : {},
|
|
6825
|
+
...applied.auditReference ? { auditReference: applied.auditReference } : {},
|
|
6826
|
+
...applied.commandReference ? { commandReference: applied.commandReference } : {}
|
|
6827
|
+
};
|
|
6828
|
+
}
|
|
6829
|
+
function runPlanTableActionStep(runtime, mode, step, plan) {
|
|
6830
|
+
const before = readPlanTableAction(runtime, step.actionHandle);
|
|
6831
|
+
if (!before.ok) {
|
|
6832
|
+
return blockedPlanStepFromDetails(step.id, step.kind, before.blockerDetails);
|
|
6833
|
+
}
|
|
6834
|
+
const tableAction = before.tableAction;
|
|
6835
|
+
if (!tableAction) {
|
|
6836
|
+
return blockedPlanStep(
|
|
6837
|
+
step.id,
|
|
6838
|
+
step.kind,
|
|
6839
|
+
`actions:plan:action-handle-not-found:${step.actionHandle}`,
|
|
6840
|
+
"unresolved-target",
|
|
6841
|
+
"No current table action matches the supplied opaque actionHandle.",
|
|
6842
|
+
"Have the host re-list table actions and regenerate the plan with a fresh actionHandle."
|
|
6843
|
+
);
|
|
6844
|
+
}
|
|
6845
|
+
const precondition = checkPlanPreconditions(step, before.readback);
|
|
6846
|
+
if (precondition) {
|
|
6847
|
+
return blockedPlanStepFromDetails(step.id, step.kind, [precondition], {
|
|
6848
|
+
beforeReadback: before.readback,
|
|
6849
|
+
tableAction
|
|
6850
|
+
});
|
|
6851
|
+
}
|
|
6852
|
+
const operationKind = step.operation?.kind;
|
|
6853
|
+
const operationFamilyBlocker = tablePlanOperationFamilyBlocker(step.kind, operationKind);
|
|
6854
|
+
if (operationFamilyBlocker) {
|
|
6855
|
+
return blockedPlanStepFromDetails(step.id, step.kind, [operationFamilyBlocker], {
|
|
6856
|
+
beforeReadback: before.readback,
|
|
6857
|
+
tableAction
|
|
6858
|
+
});
|
|
6859
|
+
}
|
|
6860
|
+
if (!operationKind || !tableAction.callableOperations.includes(operationKind)) {
|
|
6861
|
+
return blockedPlanStepFromDetails(
|
|
6862
|
+
step.id,
|
|
6863
|
+
step.kind,
|
|
6864
|
+
[
|
|
6865
|
+
blocker(
|
|
6866
|
+
`actions:plan:table-operation-unsupported:${step.id}`,
|
|
6867
|
+
"unsupported",
|
|
6868
|
+
"The table action handle does not advertise the requested operation as callable.",
|
|
6869
|
+
"Have the host re-list table actions and choose an operation from callableOperations."
|
|
6870
|
+
)
|
|
6871
|
+
],
|
|
6872
|
+
{
|
|
6873
|
+
beforeReadback: before.readback,
|
|
6874
|
+
tableAction
|
|
6875
|
+
}
|
|
6876
|
+
);
|
|
6877
|
+
}
|
|
6878
|
+
if (mode === "preview") {
|
|
6879
|
+
return {
|
|
6880
|
+
id: step.id,
|
|
6881
|
+
kind: step.kind,
|
|
6882
|
+
status: "planned",
|
|
6883
|
+
applied: false,
|
|
6884
|
+
changed: false,
|
|
6885
|
+
tableAction,
|
|
6886
|
+
...before.readback ? { beforeReadback: before.readback } : {}
|
|
6887
|
+
};
|
|
6888
|
+
}
|
|
6889
|
+
const result = createTableActionFamily(runtime).applyTableAction({
|
|
6890
|
+
actionHandle: step.actionHandle,
|
|
6891
|
+
operation: step.operation,
|
|
6892
|
+
...step.scopeId ? { scopeId: step.scopeId } : {},
|
|
6893
|
+
actorId: step.actorId ?? plan.actorId,
|
|
6894
|
+
origin: step.origin ?? plan.origin,
|
|
6895
|
+
...step.proposalId ? { proposalId: step.proposalId } : {}
|
|
6896
|
+
});
|
|
6897
|
+
const after = readPlanTableAction(runtime, step.actionHandle);
|
|
6898
|
+
return {
|
|
6899
|
+
id: step.id,
|
|
6900
|
+
kind: step.kind,
|
|
6901
|
+
status: result.applied ? "applied" : "blocked",
|
|
6902
|
+
applied: result.applied,
|
|
6903
|
+
changed: result.changed,
|
|
6904
|
+
tableAction,
|
|
6905
|
+
...before.readback ? { beforeReadback: before.readback } : {},
|
|
6906
|
+
...result.afterReadback ? { afterReadback: tableReadbackToPlanReadback(result.afterReadback) } : after.ok && after.readback ? { afterReadback: after.readback } : {},
|
|
6907
|
+
proposalId: result.proposalId,
|
|
6908
|
+
...result.posture ? { posture: result.posture } : {},
|
|
6909
|
+
...result.blockers ? { blockers: result.blockers } : {},
|
|
6910
|
+
...result.blockerDetails ? {
|
|
6911
|
+
blockerDetails: Object.freeze(
|
|
6912
|
+
result.blockerDetails.map(
|
|
6913
|
+
(detail) => blocker(detail.code, "blocked", detail.message, detail.nextStep)
|
|
6914
|
+
)
|
|
6915
|
+
)
|
|
6916
|
+
} : {},
|
|
6917
|
+
...result.commandReference ? { commandReference: result.commandReference } : {}
|
|
6918
|
+
};
|
|
6919
|
+
}
|
|
6920
|
+
function locateAll(runtime, input) {
|
|
6921
|
+
if (!input.query) {
|
|
6922
|
+
const detail = blocker(
|
|
6923
|
+
"actions:locate:query-required",
|
|
6924
|
+
"input",
|
|
6925
|
+
"Locate actions require a non-empty query.",
|
|
6926
|
+
"Retry with a non-empty query string."
|
|
6927
|
+
);
|
|
6928
|
+
return {
|
|
6929
|
+
status: "blocked",
|
|
6930
|
+
matches: Object.freeze([]),
|
|
6931
|
+
blockers: Object.freeze([detail.code]),
|
|
6932
|
+
blockerDetails: Object.freeze([detail])
|
|
6933
|
+
};
|
|
6934
|
+
}
|
|
6935
|
+
const compiler = createScopeCompilerService(runtime);
|
|
6936
|
+
const limit = Math.max(0, input.limit ?? DEFAULT_LOCATE_LIMIT);
|
|
6937
|
+
const matches = [];
|
|
6938
|
+
const scopes = compiler.compileAllScopes();
|
|
6939
|
+
const shouldScanTableText = input.includeTableText === true || input.includeTableText !== false && input.kind !== void 0 && isTableFamilyScope(input.kind);
|
|
6940
|
+
for (const scope of scopes) {
|
|
6941
|
+
if (matches.length >= limit) break;
|
|
6942
|
+
if (input.kind && scope.kind !== input.kind) continue;
|
|
6943
|
+
if (isTableFamilyScope(scope.kind)) continue;
|
|
6944
|
+
const text = scope.content.text;
|
|
6945
|
+
if (!textMatches(text, input.query, input.matchCase)) continue;
|
|
6946
|
+
matches.push({
|
|
6947
|
+
kind: scope.kind,
|
|
6948
|
+
text,
|
|
6949
|
+
excerpt: scope.content.excerpt ?? excerpt(text),
|
|
6950
|
+
handle: scope.handle,
|
|
6951
|
+
isEmpty: text.trim().length === 0
|
|
6952
|
+
});
|
|
6953
|
+
}
|
|
6954
|
+
if (matches.length < limit && shouldScanTableText) {
|
|
6955
|
+
const tableTextScopeLimit = Math.max(
|
|
6956
|
+
0,
|
|
6957
|
+
input.tableTextScopeLimit ?? DEFAULT_TABLE_TEXT_SCOPE_LIMIT
|
|
6958
|
+
);
|
|
6959
|
+
let tableScopesScanned = 0;
|
|
6960
|
+
for (const scope of scopes) {
|
|
6961
|
+
if (matches.length >= limit || tableScopesScanned >= tableTextScopeLimit) break;
|
|
6962
|
+
if (scope.kind !== "table") continue;
|
|
6963
|
+
tableScopesScanned += 1;
|
|
6964
|
+
for (const action of tableTextActionsForScope(runtime, scope.handle)) {
|
|
6965
|
+
if (matches.length >= limit) break;
|
|
6966
|
+
const readback = action.readback;
|
|
6967
|
+
if (!readback || !textMatches(readback.text, input.query, input.matchCase)) continue;
|
|
6968
|
+
matches.push({
|
|
6969
|
+
kind: "table-text",
|
|
6970
|
+
text: readback.text,
|
|
6971
|
+
excerpt: excerpt(readback.text),
|
|
6972
|
+
actionHandle: action.actionHandle,
|
|
6973
|
+
readback,
|
|
6974
|
+
isEmpty: readback.isEmpty
|
|
6975
|
+
});
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6979
|
+
return {
|
|
6980
|
+
status: matches.length > 0 ? "found" : "not-found",
|
|
6981
|
+
matches: Object.freeze(matches),
|
|
6982
|
+
...matches.length === 0 ? { blockers: Object.freeze([`actions:locate:not-found:${input.query}`]) } : {}
|
|
6983
|
+
};
|
|
6984
|
+
}
|
|
6985
|
+
function resolveTarget(runtime, target) {
|
|
6986
|
+
if ("actionHandle" in target) {
|
|
6987
|
+
const action = findTableAction(runtime, target.actionHandle);
|
|
6988
|
+
if (!action) {
|
|
6989
|
+
return blockedResolution(
|
|
6990
|
+
`actions:target:action-handle-not-found:${target.actionHandle}`,
|
|
6991
|
+
"unresolved-target",
|
|
6992
|
+
"No current table text action matches the supplied actionHandle.",
|
|
6993
|
+
"Call ai.actions.locateAll or ai.listTableActions again and retry with a fresh actionHandle."
|
|
6994
|
+
);
|
|
6995
|
+
}
|
|
6996
|
+
if (action.family !== "table-text") {
|
|
6997
|
+
return blockedResolution(
|
|
6998
|
+
`actions:target:action-handle-not-text:${target.actionHandle}`,
|
|
6999
|
+
"unsupported",
|
|
7000
|
+
"This actionHandle is not a table text action.",
|
|
7001
|
+
"Use table structure handles with ai.applyTableAction, or use a table text actionHandle for rewrite."
|
|
7002
|
+
);
|
|
7003
|
+
}
|
|
7004
|
+
return { ok: true, target: { kind: "table-text", action } };
|
|
7005
|
+
}
|
|
7006
|
+
if ("handle" in target) {
|
|
7007
|
+
const compiled = createScopeCompilerService(runtime).compileScopeById(target.handle.scopeId);
|
|
7008
|
+
if (!compiled) {
|
|
7009
|
+
return blockedResolution(
|
|
7010
|
+
`actions:target:scope-not-resolvable:${target.handle.scopeId}`,
|
|
7011
|
+
"unresolved-target",
|
|
7012
|
+
"The target scope no longer resolves in the current document.",
|
|
7013
|
+
"Refresh the scope handle with ai.actions.locateAll or ai.resolveReference, then retry."
|
|
7014
|
+
);
|
|
7015
|
+
}
|
|
7016
|
+
return { ok: true, target: { kind: "scope", scope: compiled.scope, handle: compiled.scope.handle } };
|
|
7017
|
+
}
|
|
7018
|
+
const located = locateAll(runtime, {
|
|
7019
|
+
query: target.query,
|
|
7020
|
+
kind: target.kind,
|
|
7021
|
+
matchCase: target.matchCase,
|
|
7022
|
+
includeTableText: target.includeTableText,
|
|
7023
|
+
tableTextScopeLimit: target.tableTextScopeLimit,
|
|
7024
|
+
limit: 2
|
|
7025
|
+
});
|
|
7026
|
+
if (located.status !== "found") {
|
|
7027
|
+
return blockedResolution(
|
|
7028
|
+
located.blockers?.[0] ?? `actions:target:not-found:${target.query}`,
|
|
7029
|
+
located.status === "blocked" ? "input" : "unresolved-target",
|
|
7030
|
+
located.status === "blocked" ? "The target query was blocked." : "No unique target matched the query.",
|
|
7031
|
+
"Call ai.actions.locateAll and retry with a returned handle or actionHandle.",
|
|
7032
|
+
located.blockerDetails
|
|
7033
|
+
);
|
|
7034
|
+
}
|
|
7035
|
+
if (located.matches.length > 1) {
|
|
7036
|
+
return blockedResolution(
|
|
7037
|
+
`actions:target:ambiguous:${target.query}`,
|
|
7038
|
+
"ambiguous-target",
|
|
7039
|
+
"The query matched more than one target.",
|
|
7040
|
+
"Call ai.actions.locateAll and retry with the exact returned handle or actionHandle."
|
|
7041
|
+
);
|
|
7042
|
+
}
|
|
7043
|
+
const [match] = located.matches;
|
|
7044
|
+
if (!match) {
|
|
7045
|
+
return blockedResolution(
|
|
7046
|
+
`actions:target:not-found:${target.query}`,
|
|
7047
|
+
"unresolved-target",
|
|
7048
|
+
"No target matched the query.",
|
|
7049
|
+
"Call ai.actions.locateAll with a broader query."
|
|
7050
|
+
);
|
|
7051
|
+
}
|
|
7052
|
+
if (match.actionHandle) return resolveTarget(runtime, { actionHandle: match.actionHandle });
|
|
7053
|
+
if (match.handle) return resolveTarget(runtime, { handle: match.handle });
|
|
7054
|
+
return blockedResolution(
|
|
7055
|
+
`actions:target:not-callable:${target.query}`,
|
|
7056
|
+
"blocked",
|
|
7057
|
+
"The locate match did not carry a callable target.",
|
|
7058
|
+
"Refresh the locate result and retry."
|
|
7059
|
+
);
|
|
7060
|
+
}
|
|
7061
|
+
function applyRewrite(runtime, target, input) {
|
|
7062
|
+
if (target.kind === "table-text") {
|
|
7063
|
+
const result2 = createTableActionFamily(runtime).applyTableAction({
|
|
7064
|
+
actionHandle: target.action.actionHandle,
|
|
7065
|
+
operation: { kind: "paste-cell-content", content: { kind: "text", text: input.text } },
|
|
7066
|
+
actorId: input.actorId,
|
|
7067
|
+
origin: input.origin,
|
|
7068
|
+
proposalId: input.proposalId ?? mockId(runtime.getSessionState().documentId, `actions-rewrite-${target.action.actionHandle}`)
|
|
7069
|
+
});
|
|
7070
|
+
return {
|
|
7071
|
+
status: result2.applied ? "applied" : "blocked",
|
|
7072
|
+
applied: result2.applied,
|
|
7073
|
+
changed: result2.changed,
|
|
7074
|
+
target: summarizeTarget(target),
|
|
7075
|
+
proposalId: result2.proposalId,
|
|
7076
|
+
posture: result2.posture,
|
|
7077
|
+
...result2.blockers ? { blockers: result2.blockers } : {},
|
|
7078
|
+
...result2.blockerDetails ? {
|
|
7079
|
+
blockerDetails: Object.freeze(
|
|
7080
|
+
result2.blockerDetails.map(
|
|
7081
|
+
(detail) => blocker(detail.code, "blocked", detail.message, detail.nextStep)
|
|
7082
|
+
)
|
|
7083
|
+
)
|
|
7084
|
+
} : {},
|
|
7085
|
+
...result2.commandReference ? { commandReference: result2.commandReference } : {},
|
|
7086
|
+
...result2.afterReadback ? { afterReadback: result2.afterReadback } : {}
|
|
7087
|
+
};
|
|
7088
|
+
}
|
|
7089
|
+
const beforeText = target.scope.content.text;
|
|
7090
|
+
const result = createReplacementFamily(runtime).applyReplacementScope({
|
|
7091
|
+
targetScopeId: target.handle.scopeId,
|
|
7092
|
+
operation: "replace",
|
|
7093
|
+
proposedText: input.text,
|
|
7094
|
+
...input.actionId ? { actionId: input.actionId } : {},
|
|
7095
|
+
...input.reason ? { reason: input.reason } : {},
|
|
7096
|
+
...input.preserve ? { preserve: input.preserve } : {},
|
|
7097
|
+
...input.actorId ? { actorId: input.actorId } : {},
|
|
7098
|
+
...input.origin ? { origin: input.origin } : {},
|
|
7099
|
+
...input.proposalId ? { proposalId: input.proposalId } : {}
|
|
7100
|
+
});
|
|
7101
|
+
return projectRewriteScopeResult(runtime, result, target, beforeText, input.text);
|
|
7102
|
+
}
|
|
7103
|
+
function projectApplyResult(result, target) {
|
|
7104
|
+
return {
|
|
7105
|
+
status: result.applied ? "applied" : "blocked",
|
|
7106
|
+
applied: result.applied,
|
|
7107
|
+
changed: result.applied,
|
|
7108
|
+
target: summarizeTarget(target),
|
|
7109
|
+
proposalId: result.proposalId,
|
|
7110
|
+
...result.posture ? { posture: result.posture } : {},
|
|
7111
|
+
...result.blockers ? { blockers: result.blockers } : {},
|
|
7112
|
+
...result.blockerDetails ? {
|
|
7113
|
+
blockerDetails: Object.freeze(
|
|
7114
|
+
result.blockerDetails.map(
|
|
7115
|
+
(detail) => blocker(detail.code, "blocked", detail.message, detail.nextStep)
|
|
7116
|
+
)
|
|
7117
|
+
)
|
|
7118
|
+
} : {},
|
|
7119
|
+
...result.auditReference ? { auditReference: result.auditReference } : {}
|
|
7120
|
+
};
|
|
7121
|
+
}
|
|
7122
|
+
function projectRewriteScopeResult(runtime, result, target, beforeText, proposedText) {
|
|
7123
|
+
if (!result.applied) return projectApplyResult(result, target);
|
|
7124
|
+
const compiledAfter = createScopeCompilerService(runtime).compileScopeById(
|
|
7125
|
+
target.handle.scopeId
|
|
7126
|
+
);
|
|
7127
|
+
if (!compiledAfter) {
|
|
7128
|
+
return blockedRewriteReadback(
|
|
7129
|
+
result,
|
|
7130
|
+
target,
|
|
7131
|
+
`actions:rewrite:authoritative-readback-unresolvable:${target.handle.scopeId}`,
|
|
7132
|
+
"The replacement primitive reported success, but the target scope could not be re-read afterwards.",
|
|
7133
|
+
"Treat the mutation as untrusted. Re-locate the target and retry only after the scope resolves, or create an issue flag instead."
|
|
7134
|
+
);
|
|
7135
|
+
}
|
|
7136
|
+
const afterText = compiledAfter.scope.content.text;
|
|
7137
|
+
const changed = afterText !== beforeText;
|
|
7138
|
+
const expectedPresent = proposedText.length === 0 ? afterText.length === 0 : afterText.includes(proposedText);
|
|
7139
|
+
if (!changed) {
|
|
7140
|
+
return blockedRewriteReadback(
|
|
7141
|
+
result,
|
|
7142
|
+
{ ...target, scope: compiledAfter.scope, handle: compiledAfter.scope.handle },
|
|
7143
|
+
`actions:rewrite:authoritative-readback-unchanged:${target.handle.scopeId}`,
|
|
7144
|
+
"The replacement primitive reported success, but authoritative scope readback showed unchanged text.",
|
|
7145
|
+
"Treat the mutation as not applied. Retry with a narrower scope or create an issue flag; do not claim the replacement succeeded."
|
|
7146
|
+
);
|
|
7147
|
+
}
|
|
7148
|
+
if (!expectedPresent) {
|
|
7149
|
+
return blockedRewriteReadback(
|
|
7150
|
+
result,
|
|
7151
|
+
{ ...target, scope: compiledAfter.scope, handle: compiledAfter.scope.handle },
|
|
7152
|
+
`actions:rewrite:authoritative-readback-mismatch:${target.handle.scopeId}`,
|
|
7153
|
+
"The replacement primitive reported success, but authoritative scope readback did not contain the proposed text.",
|
|
7154
|
+
"Treat the mutation as suspect. Re-read the target, inspect the exported package when available, and retry only with a verified target."
|
|
7155
|
+
);
|
|
7156
|
+
}
|
|
7157
|
+
return {
|
|
7158
|
+
status: "applied",
|
|
7159
|
+
applied: true,
|
|
7160
|
+
changed: true,
|
|
7161
|
+
target: summarizeTarget({
|
|
7162
|
+
...target,
|
|
7163
|
+
scope: compiledAfter.scope,
|
|
7164
|
+
handle: compiledAfter.scope.handle
|
|
7165
|
+
}),
|
|
7166
|
+
proposalId: result.proposalId,
|
|
7167
|
+
...result.posture ? { posture: result.posture } : {},
|
|
7168
|
+
...result.auditReference ? { auditReference: result.auditReference } : {}
|
|
7169
|
+
};
|
|
7170
|
+
}
|
|
7171
|
+
function blockedRewriteReadback(result, target, code, message, nextStep) {
|
|
7172
|
+
const detail = blocker(code, "blocked", message, nextStep);
|
|
7173
|
+
return {
|
|
7174
|
+
status: "blocked",
|
|
7175
|
+
applied: false,
|
|
7176
|
+
changed: false,
|
|
7177
|
+
target: summarizeTarget(target),
|
|
7178
|
+
proposalId: result.proposalId,
|
|
7179
|
+
posture: "suspect-readback",
|
|
7180
|
+
blockers: Object.freeze([detail.code]),
|
|
7181
|
+
blockerDetails: Object.freeze([detail]),
|
|
7182
|
+
...result.auditReference ? { auditReference: result.auditReference } : {}
|
|
7183
|
+
};
|
|
7184
|
+
}
|
|
7185
|
+
function summarizeTarget(target) {
|
|
7186
|
+
if (target.kind === "table-text") {
|
|
7187
|
+
return {
|
|
7188
|
+
kind: "table-text",
|
|
7189
|
+
actionHandle: target.action.actionHandle,
|
|
7190
|
+
...target.action.readback ? { readback: target.action.readback } : {},
|
|
7191
|
+
canRewriteText: true,
|
|
7192
|
+
canInsertAdjacentText: false,
|
|
7193
|
+
canFlag: false,
|
|
7194
|
+
canMark: false
|
|
7195
|
+
};
|
|
7196
|
+
}
|
|
7197
|
+
return {
|
|
7198
|
+
kind: target.scope.kind,
|
|
7199
|
+
handle: target.handle,
|
|
7200
|
+
canRewriteText: canRewriteScopeText(target.scope),
|
|
7201
|
+
canInsertAdjacentText: canRewriteScopeText(target.scope),
|
|
7202
|
+
canFlag: true,
|
|
7203
|
+
canMark: canMarkScope(target.scope)
|
|
7204
|
+
};
|
|
7205
|
+
}
|
|
7206
|
+
function tableTextActionsForScope(runtime, handle) {
|
|
7207
|
+
const result = createTableActionFamily(runtime).listTableActions({
|
|
7208
|
+
handle,
|
|
7209
|
+
nowUtc: currentAuditTimestamp(runtime)
|
|
7210
|
+
});
|
|
7211
|
+
return result.actions.filter((action) => action.family === "table-text");
|
|
7212
|
+
}
|
|
7213
|
+
function findTableAction(runtime, actionHandle) {
|
|
7214
|
+
const action = findAnyTableAction(runtime, actionHandle);
|
|
7215
|
+
return action?.family === "table-text" ? action : null;
|
|
7216
|
+
}
|
|
7217
|
+
function findAnyTableAction(runtime, actionHandle) {
|
|
7218
|
+
const compiler = createScopeCompilerService(runtime);
|
|
7219
|
+
for (const scope of compiler.compileAllScopes()) {
|
|
7220
|
+
if (scope.kind !== "table") continue;
|
|
7221
|
+
const result = createTableActionFamily(runtime).listTableActions({
|
|
7222
|
+
handle: scope.handle,
|
|
7223
|
+
nowUtc: currentAuditTimestamp(runtime)
|
|
7224
|
+
});
|
|
7225
|
+
const action = result.actions.find(
|
|
7226
|
+
(candidate) => candidate.actionHandle === actionHandle
|
|
7227
|
+
);
|
|
7228
|
+
if (action) return action;
|
|
7229
|
+
}
|
|
7230
|
+
return null;
|
|
7231
|
+
}
|
|
7232
|
+
function readDocumentPlanTarget(runtime) {
|
|
7233
|
+
const document = createInspectFamily(runtime).inspectDocument();
|
|
7234
|
+
return {
|
|
7235
|
+
ok: true,
|
|
7236
|
+
readback: {
|
|
7237
|
+
text: document.semanticSummary,
|
|
7238
|
+
excerpt: document.semanticSummary,
|
|
7239
|
+
isEmpty: document.semanticSummary.trim().length === 0
|
|
7240
|
+
}
|
|
7241
|
+
};
|
|
7242
|
+
}
|
|
7243
|
+
function readPlanTarget(runtime, target) {
|
|
7244
|
+
if ("handle" in target) {
|
|
7245
|
+
const compiled = createScopeCompilerService(runtime).compileScopeById(target.handle.scopeId);
|
|
7246
|
+
if (!compiled) {
|
|
7247
|
+
return {
|
|
7248
|
+
ok: false,
|
|
7249
|
+
blockerDetails: Object.freeze([
|
|
7250
|
+
blocker(
|
|
7251
|
+
`actions:plan:target-not-resolvable:${target.handle.scopeId}`,
|
|
7252
|
+
"unresolved-target",
|
|
7253
|
+
"The plan step scope handle no longer resolves.",
|
|
7254
|
+
"Have the host re-locate the target and regenerate the plan with a fresh handle."
|
|
7255
|
+
)
|
|
7256
|
+
])
|
|
7257
|
+
};
|
|
7258
|
+
}
|
|
7259
|
+
const text = compiled.scope.content.text;
|
|
7260
|
+
const resolved = {
|
|
7261
|
+
kind: "scope",
|
|
7262
|
+
scope: compiled.scope,
|
|
7263
|
+
handle: compiled.scope.handle
|
|
7264
|
+
};
|
|
7265
|
+
return {
|
|
7266
|
+
ok: true,
|
|
7267
|
+
target: summarizeTarget(resolved),
|
|
7268
|
+
readback: {
|
|
7269
|
+
text,
|
|
7270
|
+
excerpt: compiled.scope.content.excerpt ?? excerpt(text),
|
|
7271
|
+
isEmpty: text.trim().length === 0
|
|
7272
|
+
}
|
|
7273
|
+
};
|
|
7274
|
+
}
|
|
7275
|
+
if (!("actionHandle" in target) || typeof target.actionHandle !== "string") {
|
|
7276
|
+
return {
|
|
7277
|
+
ok: false,
|
|
7278
|
+
blockerDetails: Object.freeze([
|
|
7279
|
+
blocker(
|
|
7280
|
+
"actions:plan:exact-target-required",
|
|
7281
|
+
"input",
|
|
7282
|
+
"Plan steps accept only exact ScopeHandle or opaque actionHandle targets.",
|
|
7283
|
+
"Have the host resolve queries before calling runPlan and pass only returned handles."
|
|
7284
|
+
)
|
|
7285
|
+
])
|
|
7286
|
+
};
|
|
7287
|
+
}
|
|
7288
|
+
return readPlanTableAction(runtime, target.actionHandle);
|
|
7289
|
+
}
|
|
7290
|
+
function readPlanTableAction(runtime, actionHandle) {
|
|
7291
|
+
const action = findAnyTableAction(runtime, actionHandle);
|
|
7292
|
+
if (!action) {
|
|
7293
|
+
return {
|
|
7294
|
+
ok: false,
|
|
7295
|
+
blockerDetails: Object.freeze([
|
|
7296
|
+
blocker(
|
|
7297
|
+
`actions:plan:action-handle-not-found:${actionHandle}`,
|
|
7298
|
+
"unresolved-target",
|
|
7299
|
+
"No current table action matches the supplied opaque actionHandle.",
|
|
7300
|
+
"Have the host call ai.listTableActions or ai.actions.locateAll again and regenerate the plan with a fresh actionHandle."
|
|
7301
|
+
)
|
|
7302
|
+
])
|
|
7303
|
+
};
|
|
7304
|
+
}
|
|
7305
|
+
return {
|
|
7306
|
+
ok: true,
|
|
7307
|
+
tableAction: action,
|
|
7308
|
+
...action.family === "table-text" ? { target: summarizeTarget({ kind: "table-text", action }) } : {},
|
|
7309
|
+
...action.readback ? { readback: tableReadbackToPlanReadback(action.readback) } : {}
|
|
7310
|
+
};
|
|
7311
|
+
}
|
|
7312
|
+
function tableReadbackToPlanReadback(readback) {
|
|
7313
|
+
return {
|
|
7314
|
+
text: readback.text,
|
|
7315
|
+
excerpt: excerpt(readback.text),
|
|
7316
|
+
isEmpty: readback.isEmpty
|
|
7317
|
+
};
|
|
7318
|
+
}
|
|
7319
|
+
function checkPlanPreconditions(step, readback) {
|
|
7320
|
+
const preconditions = step.preconditions;
|
|
7321
|
+
if (!preconditions) return null;
|
|
7322
|
+
if (preconditions.expectedBeforeTextIncludes !== void 0 && !readback?.text?.includes(preconditions.expectedBeforeTextIncludes)) {
|
|
7323
|
+
return blocker(
|
|
7324
|
+
`actions:plan:precondition-text-mismatch:${step.id}`,
|
|
7325
|
+
"blocked",
|
|
7326
|
+
"The target readback did not contain the host-provided expected text before the step.",
|
|
7327
|
+
"Re-read the target and regenerate the plan from current document state."
|
|
7328
|
+
);
|
|
7329
|
+
}
|
|
7330
|
+
if (preconditions.expectedBeforeIsEmpty !== void 0 && readback?.isEmpty !== preconditions.expectedBeforeIsEmpty) {
|
|
7331
|
+
return blocker(
|
|
7332
|
+
`actions:plan:precondition-empty-mismatch:${step.id}`,
|
|
7333
|
+
"blocked",
|
|
7334
|
+
"The target empty/non-empty state no longer matches the host-provided precondition.",
|
|
7335
|
+
"Re-read the target and regenerate the plan from current document state."
|
|
7336
|
+
);
|
|
7337
|
+
}
|
|
7338
|
+
return null;
|
|
7339
|
+
}
|
|
7340
|
+
function checkPlanStepCapability(runtime, step, before) {
|
|
7341
|
+
const target = before.target;
|
|
7342
|
+
if (!target) {
|
|
7343
|
+
return blocker(
|
|
7344
|
+
`actions:plan:target-required:${step.id}`,
|
|
7345
|
+
"input",
|
|
7346
|
+
"The plan step requires an exact target handle.",
|
|
7347
|
+
"Regenerate the plan with a ScopeHandle or opaque actionHandle returned by L09."
|
|
7348
|
+
);
|
|
7349
|
+
}
|
|
7350
|
+
if (step.kind === "rewrite" && !target.canRewriteText) {
|
|
7351
|
+
return blocker(
|
|
7352
|
+
`actions:plan:target-not-rewriteable:${step.id}`,
|
|
7353
|
+
"unsupported",
|
|
7354
|
+
"The target capability posture does not allow text rewrite.",
|
|
7355
|
+
"Use a supported text target, table text actionHandle, or create an issue flag instead."
|
|
7356
|
+
);
|
|
7357
|
+
}
|
|
7358
|
+
if (step.kind === "insertText" && !target.canInsertAdjacentText) {
|
|
7359
|
+
return blocker(
|
|
7360
|
+
`actions:plan:target-not-insertable:${step.id}`,
|
|
7361
|
+
"unsupported",
|
|
7362
|
+
"The target capability posture does not allow adjacent text insertion.",
|
|
7363
|
+
"Use a supported semantic scope or choose rewrite/flag instead."
|
|
7364
|
+
);
|
|
7365
|
+
}
|
|
7366
|
+
if (step.kind === "flag" && !target.canFlag) {
|
|
7367
|
+
return blocker(
|
|
7368
|
+
`actions:plan:target-not-flaggable:${step.id}`,
|
|
7369
|
+
"unsupported",
|
|
7370
|
+
"The target capability posture does not allow issue flags.",
|
|
7371
|
+
"Use the owning semantic scope handle for issue flags."
|
|
7372
|
+
);
|
|
7373
|
+
}
|
|
7374
|
+
if (step.kind === "mark" && !target.canMark) {
|
|
7375
|
+
return blocker(
|
|
7376
|
+
`actions:plan:target-not-markable:${step.id}`,
|
|
7377
|
+
"unsupported",
|
|
7378
|
+
"The target capability posture does not allow formatting marks.",
|
|
7379
|
+
"Use a supported semantic scope or create an issue flag instead."
|
|
7380
|
+
);
|
|
7381
|
+
}
|
|
7382
|
+
if (step.kind === "fieldRefresh" || step.kind === "tocRefresh" || step.kind === "bookmarkEdit" || step.kind === "hyperlinkDestinationEdit") {
|
|
7383
|
+
const resolved = resolveScopeExactTarget(runtime, step.target, step.kind);
|
|
7384
|
+
if (!resolved.ok) return resolved.blockerDetails[0] ?? null;
|
|
7385
|
+
const targetRef = resolveModeledEditableTarget(runtime, resolved.target, step.kind);
|
|
7386
|
+
if (!targetRef.ok) return targetRef.blockerDetails[0] ?? null;
|
|
7387
|
+
return null;
|
|
7388
|
+
}
|
|
7389
|
+
if (step.kind === "listOperation") {
|
|
7390
|
+
const resolved = resolveScopeExactTarget(runtime, step.target, step.kind);
|
|
7391
|
+
if (!resolved.ok) return resolved.blockerDetails[0] ?? null;
|
|
7392
|
+
const scope = resolved.target.scope;
|
|
7393
|
+
if (scope.kind !== "list-item" && scope.kind !== "paragraph" && scope.kind !== "heading") {
|
|
7394
|
+
return blocker(
|
|
7395
|
+
`actions:list-operation:target-kind-unsupported:${scope.kind}`,
|
|
7396
|
+
"unsupported",
|
|
7397
|
+
"List operations require a paragraph-like scope handle.",
|
|
7398
|
+
"Use ai.actions.locateAll or ai.listScopes to select a paragraph, heading, or list-item handle."
|
|
7399
|
+
);
|
|
7400
|
+
}
|
|
7401
|
+
if (paragraphIndexFromHandle(scope.handle) === null) {
|
|
7402
|
+
return blockerWithOwner(
|
|
7403
|
+
`actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
|
|
7404
|
+
"blocked",
|
|
7405
|
+
"The list operation target did not resolve to a current paragraph index.",
|
|
7406
|
+
"Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
|
|
7407
|
+
"L08 semantic scope compiler"
|
|
7408
|
+
);
|
|
7409
|
+
}
|
|
7410
|
+
return null;
|
|
7411
|
+
}
|
|
7412
|
+
return null;
|
|
7413
|
+
}
|
|
7414
|
+
function applyPlanStep(runtime, step, plan) {
|
|
7415
|
+
switch (step.kind) {
|
|
7416
|
+
case "rewrite":
|
|
7417
|
+
return createActionsFamily(runtime).actions.rewrite({
|
|
7418
|
+
target: step.target,
|
|
7419
|
+
text: step.text,
|
|
7420
|
+
...step.actionId ? { actionId: step.actionId } : {},
|
|
7421
|
+
...step.reason ? { reason: step.reason } : {},
|
|
7422
|
+
...step.preserve ? { preserve: step.preserve } : {},
|
|
7423
|
+
actorId: step.actorId ?? plan.actorId,
|
|
7424
|
+
origin: step.origin ?? plan.origin,
|
|
7425
|
+
...step.proposalId ? { proposalId: step.proposalId } : {}
|
|
7426
|
+
});
|
|
7427
|
+
case "insertText":
|
|
7428
|
+
return createActionsFamily(runtime).actions.insertText({
|
|
7429
|
+
target: step.target,
|
|
7430
|
+
position: step.position,
|
|
7431
|
+
text: step.text,
|
|
7432
|
+
...step.actionId ? { actionId: step.actionId } : {},
|
|
7433
|
+
...step.reason ? { reason: step.reason } : {},
|
|
7434
|
+
...step.preserve ? { preserve: step.preserve } : {},
|
|
7435
|
+
actorId: step.actorId ?? plan.actorId,
|
|
7436
|
+
origin: step.origin ?? plan.origin,
|
|
7437
|
+
...step.proposalId ? { proposalId: step.proposalId } : {}
|
|
7438
|
+
});
|
|
7439
|
+
case "flag":
|
|
7440
|
+
return createActionsFamily(runtime).actions.flag({
|
|
7441
|
+
target: step.target,
|
|
7442
|
+
summary: step.summary,
|
|
7443
|
+
...step.severity ? { severity: step.severity } : {},
|
|
7444
|
+
actorId: step.actorId ?? plan.actorId,
|
|
7445
|
+
origin: step.origin ?? plan.origin
|
|
7446
|
+
});
|
|
7447
|
+
case "mark":
|
|
7448
|
+
return createActionsFamily(runtime).actions.mark({
|
|
7449
|
+
target: step.target,
|
|
7450
|
+
action: step.action,
|
|
7451
|
+
...step.actionId ? { actionId: step.actionId } : {},
|
|
7452
|
+
...step.reason ? { reason: step.reason } : {},
|
|
7453
|
+
actorId: step.actorId ?? plan.actorId,
|
|
7454
|
+
origin: step.origin ?? plan.origin,
|
|
7455
|
+
...step.proposalId ? { proposalId: step.proposalId } : {}
|
|
7456
|
+
});
|
|
7457
|
+
case "fieldRefresh":
|
|
7458
|
+
return createActionsFamily(runtime).actions.fieldRefresh({
|
|
7459
|
+
target: step.target,
|
|
7460
|
+
supportedOnly: step.supportedOnly,
|
|
7461
|
+
actorId: step.actorId ?? plan.actorId,
|
|
7462
|
+
origin: step.origin ?? plan.origin
|
|
7463
|
+
});
|
|
7464
|
+
case "tocRefresh":
|
|
7465
|
+
return createActionsFamily(runtime).actions.tocRefresh({
|
|
7466
|
+
target: step.target,
|
|
7467
|
+
...step.tocId ? { tocId: step.tocId } : {},
|
|
7468
|
+
...step.mode ? { mode: step.mode } : {},
|
|
7469
|
+
...step.maxLevel !== void 0 ? { maxLevel: step.maxLevel } : {},
|
|
7470
|
+
actorId: step.actorId ?? plan.actorId,
|
|
7471
|
+
origin: step.origin ?? plan.origin
|
|
7472
|
+
});
|
|
7473
|
+
case "bookmarkEdit":
|
|
7474
|
+
return createActionsFamily(runtime).actions.bookmarkEdit({
|
|
7475
|
+
target: step.target,
|
|
7476
|
+
text: step.text,
|
|
7477
|
+
actorId: step.actorId ?? plan.actorId,
|
|
7478
|
+
origin: step.origin ?? plan.origin
|
|
7479
|
+
});
|
|
7480
|
+
case "hyperlinkDestinationEdit":
|
|
7481
|
+
return createActionsFamily(runtime).actions.hyperlinkDestinationEdit({
|
|
7482
|
+
target: step.target,
|
|
7483
|
+
href: step.href,
|
|
7484
|
+
actorId: step.actorId ?? plan.actorId,
|
|
7485
|
+
origin: step.origin ?? plan.origin
|
|
7486
|
+
});
|
|
7487
|
+
case "listOperation":
|
|
7488
|
+
return createActionsFamily(runtime).actions.listOperation({
|
|
7489
|
+
target: step.target,
|
|
7490
|
+
operation: step.operation,
|
|
7491
|
+
actorId: step.actorId ?? plan.actorId,
|
|
7492
|
+
origin: step.origin ?? plan.origin
|
|
7493
|
+
});
|
|
7494
|
+
}
|
|
7495
|
+
}
|
|
7496
|
+
function applyModeledCommand(runtime, targetInput, kind, commandInput, reference) {
|
|
7497
|
+
const resolvedScope = resolveScopeExactTarget(runtime, targetInput, kind);
|
|
7498
|
+
if (!resolvedScope.ok) return blockedApplyFromResolution(resolvedScope);
|
|
7499
|
+
const targetRef = resolveModeledEditableTarget(runtime, resolvedScope.target, kind);
|
|
7500
|
+
if (!targetRef.ok) {
|
|
7501
|
+
return blockedApplyFromResolution(targetRef);
|
|
7502
|
+
}
|
|
7503
|
+
const before = runtime.getCanonicalDocument();
|
|
7504
|
+
const command = {
|
|
7505
|
+
...commandInput,
|
|
7506
|
+
editableTarget: targetRef.targetRef
|
|
7507
|
+
};
|
|
7508
|
+
runtime.dispatch(command);
|
|
7509
|
+
const after = runtime.getCanonicalDocument();
|
|
7510
|
+
const changed = after !== before;
|
|
7511
|
+
if (!changed) {
|
|
7512
|
+
return blockedApply(
|
|
7513
|
+
`actions:${actionSlug(kind)}:runtime-noop:${resolvedScope.target.handle.scopeId}`,
|
|
7514
|
+
"blocked",
|
|
7515
|
+
"The modeled runtime command was accepted but produced no document change.",
|
|
7516
|
+
`Route to ${ownerRouteForModeledAction(kind)} with the target handle and command family evidence; do not report this as a successful warning path.`,
|
|
7517
|
+
[
|
|
7518
|
+
blockerWithOwner(
|
|
7519
|
+
`actions:${actionSlug(kind)}:runtime-noop:${resolvedScope.target.handle.scopeId}`,
|
|
7520
|
+
"blocked",
|
|
7521
|
+
"The modeled runtime command was accepted but produced no document change.",
|
|
7522
|
+
`Route to ${ownerRouteForModeledAction(kind)} with the target handle and command family evidence; do not report this as a successful warning path.`,
|
|
7523
|
+
ownerRouteForModeledAction(kind)
|
|
7524
|
+
)
|
|
7525
|
+
]
|
|
7526
|
+
);
|
|
7527
|
+
}
|
|
7528
|
+
const compiledAfter = createScopeCompilerService(runtime).compileScopeById(
|
|
7529
|
+
resolvedScope.target.handle.scopeId
|
|
7530
|
+
);
|
|
7531
|
+
const summarizedTarget = compiledAfter ? summarizeTarget({
|
|
7532
|
+
kind: "scope",
|
|
7533
|
+
scope: compiledAfter.scope,
|
|
7534
|
+
handle: compiledAfter.scope.handle
|
|
7535
|
+
}) : summarizeTarget(resolvedScope.target);
|
|
7536
|
+
return {
|
|
7537
|
+
status: "applied",
|
|
7538
|
+
applied: true,
|
|
7539
|
+
changed: true,
|
|
7540
|
+
target: summarizedTarget,
|
|
7541
|
+
commandReference: {
|
|
7542
|
+
command: command.type,
|
|
7543
|
+
actorId: reference?.actorId ?? commandActorId(commandInput.origin),
|
|
7544
|
+
origin: reference?.origin ?? commandOriginKind(commandInput.origin),
|
|
7545
|
+
emittedAtUtc: commandInput.origin?.timestamp ?? currentAuditTimestamp(runtime)
|
|
7546
|
+
}
|
|
7547
|
+
};
|
|
7548
|
+
}
|
|
7549
|
+
function applyListOperation(runtime, input) {
|
|
7550
|
+
const resolvedScope = resolveScopeExactTarget(runtime, input.target, "listOperation");
|
|
7551
|
+
if (!resolvedScope.ok) return blockedApplyFromResolution(resolvedScope);
|
|
7552
|
+
const scope = resolvedScope.target.scope;
|
|
7553
|
+
if (scope.kind !== "list-item" && scope.kind !== "paragraph" && scope.kind !== "heading") {
|
|
7554
|
+
return blockedApply(
|
|
7555
|
+
`actions:list-operation:target-kind-unsupported:${scope.kind}`,
|
|
7556
|
+
"unsupported",
|
|
7557
|
+
"List operations require a paragraph-like scope handle.",
|
|
7558
|
+
"Use ai.actions.locateAll or ai.listScopes to select a paragraph, heading, or list-item handle."
|
|
7559
|
+
);
|
|
7560
|
+
}
|
|
7561
|
+
const paragraphIndex = paragraphIndexFromHandle(scope.handle);
|
|
7562
|
+
if (paragraphIndex === null) {
|
|
7563
|
+
return blockedApply(
|
|
7564
|
+
`actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
|
|
7565
|
+
"blocked",
|
|
7566
|
+
"The list operation target did not resolve to a current paragraph index.",
|
|
7567
|
+
"Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
|
|
7568
|
+
[
|
|
7569
|
+
blockerWithOwner(
|
|
7570
|
+
`actions:list-operation:paragraph-index-unresolved:${scope.handle.scopeId}`,
|
|
7571
|
+
"blocked",
|
|
7572
|
+
"The list operation target did not resolve to a current paragraph index.",
|
|
7573
|
+
"Refresh the scope handle and retry; route persistent failures to L08 scope resolution.",
|
|
7574
|
+
"L08 semantic scope compiler"
|
|
7575
|
+
)
|
|
7576
|
+
]
|
|
7577
|
+
);
|
|
7578
|
+
}
|
|
7579
|
+
const command = listCommandForOperation(input.operation, paragraphIndex, actionOrigin(runtime, input));
|
|
7580
|
+
const before = runtime.getCanonicalDocument();
|
|
7581
|
+
runtime.dispatch(command);
|
|
7582
|
+
const changed = runtime.getCanonicalDocument() !== before;
|
|
7583
|
+
if (!changed) {
|
|
7584
|
+
return blockedApply(
|
|
7585
|
+
`actions:list-operation:runtime-noop:${scope.handle.scopeId}`,
|
|
7586
|
+
"blocked",
|
|
7587
|
+
"The list runtime command produced no document change for the selected scope.",
|
|
7588
|
+
"Select a list-compatible paragraph and retry, or route to L03/L07 list command support with the scope handle.",
|
|
7589
|
+
[
|
|
7590
|
+
blockerWithOwner(
|
|
7591
|
+
`actions:list-operation:runtime-noop:${scope.handle.scopeId}`,
|
|
7592
|
+
"blocked",
|
|
7593
|
+
"The list runtime command produced no document change for the selected scope.",
|
|
7594
|
+
"Select a list-compatible paragraph and retry, or route to L03/L07 list command support with the scope handle.",
|
|
7595
|
+
"L03 numbering/list semantics and L07 runtime list commands"
|
|
7596
|
+
)
|
|
7597
|
+
]
|
|
7598
|
+
);
|
|
7599
|
+
}
|
|
7600
|
+
const compiledAfter = createScopeCompilerService(runtime).compileScopeById(scope.handle.scopeId);
|
|
7601
|
+
return {
|
|
7602
|
+
status: "applied",
|
|
7603
|
+
applied: true,
|
|
7604
|
+
changed: true,
|
|
7605
|
+
target: compiledAfter ? summarizeTarget({ kind: "scope", scope: compiledAfter.scope, handle: compiledAfter.scope.handle }) : summarizeTarget(resolvedScope.target),
|
|
7606
|
+
commandReference: {
|
|
7607
|
+
command: command.type,
|
|
7608
|
+
actorId: input.actorId ?? "v3-ai-api",
|
|
7609
|
+
origin: input.origin ?? "agent",
|
|
7610
|
+
emittedAtUtc: command.origin?.timestamp ?? currentAuditTimestamp(runtime)
|
|
7611
|
+
}
|
|
7612
|
+
};
|
|
7613
|
+
}
|
|
7614
|
+
function projectTableApplyResult(result) {
|
|
7615
|
+
return {
|
|
7616
|
+
status: result.applied ? "applied" : "blocked",
|
|
7617
|
+
applied: result.applied,
|
|
7618
|
+
changed: result.changed,
|
|
7619
|
+
proposalId: result.proposalId,
|
|
7620
|
+
posture: result.posture,
|
|
7621
|
+
...result.blockers ? { blockers: result.blockers } : {},
|
|
7622
|
+
...result.blockerDetails ? {
|
|
7623
|
+
blockerDetails: Object.freeze(
|
|
7624
|
+
result.blockerDetails.map(
|
|
7625
|
+
(detail) => blocker(detail.code, "blocked", detail.message, detail.nextStep)
|
|
7626
|
+
)
|
|
7627
|
+
)
|
|
7628
|
+
} : {},
|
|
7629
|
+
...result.commandReference ? { commandReference: result.commandReference } : {},
|
|
7630
|
+
...result.afterReadback ? { afterReadback: result.afterReadback } : {}
|
|
7631
|
+
};
|
|
7632
|
+
}
|
|
7633
|
+
function resolveScopeExactTarget(runtime, targetInput, feature) {
|
|
7634
|
+
if ("actionHandle" in targetInput) {
|
|
7635
|
+
return blockedScopeResolution(
|
|
7636
|
+
`actions:${actionSlug(feature)}:scope-handle-required`,
|
|
7637
|
+
"unsupported",
|
|
7638
|
+
`${feature} requires a semantic scope handle, not a table actionHandle.`,
|
|
7639
|
+
"Use scope handles for generated-content, link/bookmark, and list actions; use table actionHandles only for table actions."
|
|
7640
|
+
);
|
|
7641
|
+
}
|
|
7642
|
+
const resolved = resolveTarget(runtime, { handle: targetInput.handle });
|
|
7643
|
+
if (!resolved.ok) return resolved;
|
|
7644
|
+
if (resolved.target.kind !== "scope") {
|
|
7645
|
+
return blockedScopeResolution(
|
|
7646
|
+
`actions:${actionSlug(feature)}:scope-target-required`,
|
|
7647
|
+
"unsupported",
|
|
7648
|
+
`${feature} requires a semantic scope handle.`,
|
|
7649
|
+
"Refresh the target through ai.actions.locateAll or ai.listScopes and retry with a ScopeHandle."
|
|
7650
|
+
);
|
|
7651
|
+
}
|
|
7652
|
+
return { ok: true, target: resolved.target };
|
|
7653
|
+
}
|
|
7654
|
+
function resolveModeledEditableTarget(runtime, target, kind) {
|
|
7655
|
+
const bundle = createScopeCompilerService(runtime).compileBundleById(
|
|
7656
|
+
target.handle.scopeId,
|
|
7657
|
+
currentAuditTimestamp(runtime)
|
|
7658
|
+
);
|
|
7659
|
+
const entries = bundle?.evidence.editableTargets?.entries ?? [];
|
|
7660
|
+
const candidates = entries.filter((entry) => entry.runtimeCommand.status === "supported").filter((entry) => modeledEntryMatches(kind, entry)).sort((left, right) => modeledEntryRank(kind, left) - modeledEntryRank(kind, right));
|
|
7661
|
+
if (candidates.length === 0) {
|
|
7662
|
+
return blockedEditableResolution(
|
|
7663
|
+
`actions:${actionSlug(kind)}:unsupported:${target.handle.scopeId}`,
|
|
7664
|
+
"unsupported",
|
|
7665
|
+
"The target scope does not expose a supported modeled command target for this action.",
|
|
7666
|
+
`Route to ${ownerRouteForModeledAction(kind)} with the scope handle and editable-target evidence; do not fall back to raw refs or broad replacement.`,
|
|
7667
|
+
[
|
|
7668
|
+
blockerWithOwner(
|
|
7669
|
+
`actions:${actionSlug(kind)}:unsupported:${target.handle.scopeId}`,
|
|
7670
|
+
"unsupported",
|
|
7671
|
+
"The target scope does not expose a supported modeled command target for this action.",
|
|
7672
|
+
`Route to ${ownerRouteForModeledAction(kind)} with the scope handle and editable-target evidence; do not fall back to raw refs or broad replacement.`,
|
|
7673
|
+
ownerRouteForModeledAction(kind)
|
|
7674
|
+
)
|
|
7675
|
+
]
|
|
7676
|
+
);
|
|
7677
|
+
}
|
|
7678
|
+
const [candidate] = candidates;
|
|
7679
|
+
if (!candidate) {
|
|
7680
|
+
return blockedEditableResolution(
|
|
7681
|
+
`actions:${actionSlug(kind)}:unsupported:${target.handle.scopeId}`,
|
|
7682
|
+
"unsupported",
|
|
7683
|
+
"No supported target candidate was available.",
|
|
7684
|
+
`Route to ${ownerRouteForModeledAction(kind)}.`
|
|
7685
|
+
);
|
|
7686
|
+
}
|
|
7687
|
+
const currentTarget = collectEditableTargetRefs(runtime.getCanonicalDocument()).find((ref) => ref.targetKey === candidate.targetKey);
|
|
7688
|
+
if (!currentTarget) {
|
|
7689
|
+
return blockedEditableResolution(
|
|
7690
|
+
`actions:${actionSlug(kind)}:target-not-current:${target.handle.scopeId}`,
|
|
7691
|
+
"unresolved-target",
|
|
7692
|
+
"The supported editable target evidence is stale in the current document.",
|
|
7693
|
+
"Refresh the scope handle and action evidence before retrying."
|
|
7694
|
+
);
|
|
7695
|
+
}
|
|
7696
|
+
return { ok: true, targetRef: currentTarget };
|
|
7697
|
+
}
|
|
7698
|
+
function modeledEntryMatches(kind, entry) {
|
|
7699
|
+
switch (kind) {
|
|
7700
|
+
case "fieldRefresh":
|
|
7701
|
+
return entry.commandFamily === "field" && entry.runtimeCommand.intents.includes("field-update") && entry.kind !== "toc-region-refresh";
|
|
7702
|
+
case "tocRefresh":
|
|
7703
|
+
return entry.commandFamily === "field" && entry.runtimeCommand.intents.includes("toc-refresh");
|
|
7704
|
+
case "bookmarkEdit":
|
|
7705
|
+
return entry.commandFamily === "link-bookmark" && entry.kind === "bookmark-content-range" && entry.runtimeCommand.intents.includes("bookmark-update");
|
|
7706
|
+
case "hyperlinkDestinationEdit":
|
|
7707
|
+
return entry.commandFamily === "link-bookmark" && entry.kind === "hyperlink-destination" && entry.runtimeCommand.intents.includes("hyperlink-update");
|
|
7708
|
+
}
|
|
7709
|
+
}
|
|
7710
|
+
function modeledEntryRank(kind, entry) {
|
|
7711
|
+
if (kind === "fieldRefresh" && entry.kind === "field-region-refresh") return 0;
|
|
7712
|
+
if (kind === "tocRefresh" && entry.kind === "toc-region-refresh") return 0;
|
|
7713
|
+
return 1;
|
|
7714
|
+
}
|
|
7715
|
+
function tablePlanOperationFamilyBlocker(stepKind, operationKind) {
|
|
7716
|
+
if (stepKind === "tableFragment") {
|
|
7717
|
+
if (operationKind !== "paste-cell-content" && operationKind !== "drop-cell-content") {
|
|
7718
|
+
return blocker(
|
|
7719
|
+
"actions:plan:table-fragment-operation-required",
|
|
7720
|
+
"unsupported",
|
|
7721
|
+
"tableFragment plan steps require a paste-cell-content or drop-cell-content operation.",
|
|
7722
|
+
"Use tableAction for general table operations, or supply a fragment paste/drop operation."
|
|
7723
|
+
);
|
|
7724
|
+
}
|
|
7725
|
+
}
|
|
7726
|
+
if (stepKind === "tableSelection" && operationKind !== "merge-cells" && operationKind !== "split-cell") {
|
|
7727
|
+
return blocker(
|
|
7728
|
+
"actions:plan:table-selection-operation-required",
|
|
7729
|
+
"unsupported",
|
|
7730
|
+
"tableSelection plan steps require a merge-cells or split-cell operation.",
|
|
7731
|
+
"Use tableAction for other table operations, or supply a merge/split selection operation."
|
|
7732
|
+
);
|
|
7733
|
+
}
|
|
7734
|
+
return null;
|
|
7735
|
+
}
|
|
7736
|
+
function listCommandForOperation(operation, paragraphIndex, origin) {
|
|
7737
|
+
switch (operation.kind) {
|
|
7738
|
+
case "toggle":
|
|
7739
|
+
return {
|
|
7740
|
+
type: "list.toggle",
|
|
7741
|
+
kind: operation.listKind,
|
|
7742
|
+
paragraphIndexes: [paragraphIndex],
|
|
7743
|
+
origin
|
|
7744
|
+
};
|
|
7745
|
+
case "indent":
|
|
7746
|
+
return { type: "list.indent", paragraphIndexes: [paragraphIndex], origin };
|
|
7747
|
+
case "outdent":
|
|
7748
|
+
return { type: "list.outdent", paragraphIndexes: [paragraphIndex], origin };
|
|
7749
|
+
case "restart-numbering":
|
|
7750
|
+
return {
|
|
7751
|
+
type: "list.restart-numbering",
|
|
7752
|
+
paragraphIndex,
|
|
7753
|
+
...operation.startAt !== void 0 ? { startAt: operation.startAt } : {},
|
|
7754
|
+
origin
|
|
7755
|
+
};
|
|
7756
|
+
case "continue-numbering":
|
|
7757
|
+
return { type: "list.continue-numbering", paragraphIndex, origin };
|
|
7758
|
+
}
|
|
7759
|
+
}
|
|
7760
|
+
function paragraphIndexFromHandle(handle) {
|
|
7761
|
+
const raw = handle.semanticPath[handle.semanticPath.length - 1];
|
|
7762
|
+
if (raw === void 0) return null;
|
|
7763
|
+
const index = Number(raw);
|
|
7764
|
+
return Number.isSafeInteger(index) && index >= 0 ? index : null;
|
|
7765
|
+
}
|
|
7766
|
+
function actionOrigin(runtime, input) {
|
|
7767
|
+
return { source: "api", timestamp: currentAuditTimestamp(runtime) };
|
|
7768
|
+
}
|
|
7769
|
+
function commandActorId(origin) {
|
|
7770
|
+
return origin?.source === "api" ? "v3-ai-api" : "v3-ai-api";
|
|
7771
|
+
}
|
|
7772
|
+
function commandOriginKind(_origin) {
|
|
7773
|
+
return "agent";
|
|
7774
|
+
}
|
|
7775
|
+
function actionSlug(feature) {
|
|
7776
|
+
switch (feature) {
|
|
7777
|
+
case "fieldRefresh":
|
|
7778
|
+
return "field-refresh";
|
|
7779
|
+
case "tocRefresh":
|
|
7780
|
+
return "toc-refresh";
|
|
7781
|
+
case "bookmarkEdit":
|
|
7782
|
+
return "bookmark-edit";
|
|
7783
|
+
case "hyperlinkDestinationEdit":
|
|
7784
|
+
return "hyperlink-destination-edit";
|
|
7785
|
+
case "listOperation":
|
|
7786
|
+
return "list-operation";
|
|
7787
|
+
}
|
|
7788
|
+
}
|
|
7789
|
+
function ownerRouteForModeledAction(kind) {
|
|
7790
|
+
switch (kind) {
|
|
7791
|
+
case "fieldRefresh":
|
|
7792
|
+
case "tocRefresh":
|
|
7793
|
+
return "L02 field identity, L03 generated-content semantics, L07 field refresh command";
|
|
7794
|
+
case "bookmarkEdit":
|
|
7795
|
+
case "hyperlinkDestinationEdit":
|
|
7796
|
+
return "L02 link/bookmark identity and L07 modeled link/bookmark commands";
|
|
7797
|
+
}
|
|
7798
|
+
}
|
|
7799
|
+
function isTableFamilyScope(kind) {
|
|
7800
|
+
return kind === "table" || kind === "table-row" || kind === "table-cell";
|
|
7801
|
+
}
|
|
7802
|
+
function canRewriteScopeText(scope) {
|
|
7803
|
+
return !isTableFamilyScope(scope.kind) && (scope.replaceability.level === "full" || scope.replaceability.level === "text-only");
|
|
7804
|
+
}
|
|
7805
|
+
function canMarkScope(scope) {
|
|
7806
|
+
return !isTableFamilyScope(scope.kind) && scope.replaceability.level !== "blocked" && scope.replaceability.level !== "preserve-only";
|
|
7807
|
+
}
|
|
7808
|
+
function textMatches(text, query, matchCase) {
|
|
7809
|
+
if (matchCase) return text.includes(query);
|
|
7810
|
+
return text.toLocaleLowerCase().includes(query.toLocaleLowerCase());
|
|
7811
|
+
}
|
|
7812
|
+
function excerpt(text) {
|
|
7813
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
7814
|
+
return normalized.length <= 120 ? normalized : `${normalized.slice(0, 117)}...`;
|
|
7815
|
+
}
|
|
7816
|
+
function blocker(code, category, message, nextStep) {
|
|
7817
|
+
return { code, category, message, nextStep };
|
|
7818
|
+
}
|
|
7819
|
+
function blockerWithOwner(code, category, message, nextStep, ownerRoute) {
|
|
7820
|
+
return { code, category, message, nextStep, ownerRoute };
|
|
7821
|
+
}
|
|
7822
|
+
function blockedResolution(code, category, message, nextStep, existingDetails) {
|
|
7823
|
+
const detail = existingDetails?.[0] ?? blocker(code, category, message, nextStep);
|
|
7824
|
+
return {
|
|
7825
|
+
ok: false,
|
|
7826
|
+
blockers: Object.freeze([detail.code]),
|
|
7827
|
+
blockerDetails: Object.freeze([detail])
|
|
7828
|
+
};
|
|
7829
|
+
}
|
|
7830
|
+
function blockedScopeResolution(code, category, message, nextStep, existingDetails) {
|
|
7831
|
+
const detail = existingDetails?.[0] ?? blocker(code, category, message, nextStep);
|
|
7832
|
+
return {
|
|
7833
|
+
ok: false,
|
|
7834
|
+
blockers: Object.freeze([detail.code]),
|
|
7835
|
+
blockerDetails: Object.freeze([detail])
|
|
7836
|
+
};
|
|
7837
|
+
}
|
|
7838
|
+
function blockedEditableResolution(code, category, message, nextStep, existingDetails) {
|
|
7839
|
+
const detail = existingDetails?.[0] ?? blocker(code, category, message, nextStep);
|
|
7840
|
+
return {
|
|
7841
|
+
ok: false,
|
|
7842
|
+
blockers: Object.freeze([detail.code]),
|
|
7843
|
+
blockerDetails: Object.freeze([detail])
|
|
7844
|
+
};
|
|
7845
|
+
}
|
|
7846
|
+
function blockedApply(code, category, message, nextStep, existingDetails) {
|
|
7847
|
+
const detail = existingDetails?.[0] ?? blocker(code, category, message, nextStep);
|
|
7848
|
+
return {
|
|
7849
|
+
status: category === "unsupported" ? "unsupported" : "blocked",
|
|
7850
|
+
applied: false,
|
|
7851
|
+
changed: false,
|
|
7852
|
+
blockers: Object.freeze([detail.code]),
|
|
7853
|
+
blockerDetails: Object.freeze([detail])
|
|
7854
|
+
};
|
|
7855
|
+
}
|
|
7856
|
+
function blockedApplyFromResolution(resolution) {
|
|
7857
|
+
const detail = resolution.blockerDetails[0];
|
|
7858
|
+
return {
|
|
7859
|
+
status: detail?.category === "unsupported" ? "unsupported" : "blocked",
|
|
7860
|
+
applied: false,
|
|
7861
|
+
changed: false,
|
|
7862
|
+
blockers: resolution.blockers,
|
|
7863
|
+
blockerDetails: resolution.blockerDetails
|
|
7864
|
+
};
|
|
7865
|
+
}
|
|
7866
|
+
function blockedPlan(mode, code, category, message, nextStep) {
|
|
7867
|
+
const detail = blocker(code, category, message, nextStep);
|
|
7868
|
+
return {
|
|
7869
|
+
status: "blocked",
|
|
7870
|
+
mode,
|
|
7871
|
+
applied: false,
|
|
7872
|
+
changed: false,
|
|
7873
|
+
steps: Object.freeze([]),
|
|
7874
|
+
supportedMethods: ACTION_METHODS,
|
|
7875
|
+
blockers: Object.freeze([detail.code]),
|
|
7876
|
+
blockerDetails: Object.freeze([detail])
|
|
7877
|
+
};
|
|
7878
|
+
}
|
|
7879
|
+
function blockedPlanStep(id, kind, code, category, message, nextStep) {
|
|
7880
|
+
return blockedPlanStepFromDetails(id, kind, [
|
|
7881
|
+
blocker(code, category, message, nextStep)
|
|
7882
|
+
]);
|
|
7883
|
+
}
|
|
7884
|
+
function blockedPlanStepFromDetails(id, kind, details, context) {
|
|
7885
|
+
const first = details[0] ?? blocker(
|
|
7886
|
+
`actions:plan:step-blocked:${id}`,
|
|
7887
|
+
"blocked",
|
|
7888
|
+
"The plan step was blocked.",
|
|
7889
|
+
"Inspect blockerDetails and regenerate the plan if needed."
|
|
7890
|
+
);
|
|
7891
|
+
return {
|
|
7892
|
+
id,
|
|
7893
|
+
kind,
|
|
7894
|
+
status: first.category === "unsupported" ? "unsupported" : "blocked",
|
|
7895
|
+
applied: false,
|
|
7896
|
+
changed: false,
|
|
7897
|
+
...context?.target ? { target: context.target } : {},
|
|
7898
|
+
...context?.tableAction ? { tableAction: context.tableAction } : {},
|
|
7899
|
+
...context?.beforeReadback ? { beforeReadback: context.beforeReadback } : {},
|
|
7900
|
+
blockers: Object.freeze(details.map((detail) => detail.code)),
|
|
7901
|
+
blockerDetails: Object.freeze([...details])
|
|
7902
|
+
};
|
|
7903
|
+
}
|
|
7904
|
+
function summarizePlanResult(mode, steps) {
|
|
7905
|
+
const blocked = steps.filter(
|
|
7906
|
+
(step) => step.status === "blocked" || step.status === "unsupported"
|
|
7907
|
+
);
|
|
7908
|
+
const progressed = steps.filter(
|
|
7909
|
+
(step) => step.status === "planned" || step.status === "read" || step.status === "applied"
|
|
7910
|
+
);
|
|
7911
|
+
const blockers = blocked.flatMap((step) => step.blockers ?? []);
|
|
7912
|
+
const blockerDetails = blocked.flatMap((step) => step.blockerDetails ?? []);
|
|
7913
|
+
const applied = steps.some((step) => step.applied);
|
|
7914
|
+
const changed = steps.some((step) => step.changed);
|
|
7915
|
+
return {
|
|
7916
|
+
status: blocked.length === 0 ? mode === "preview" ? "planned" : "applied" : progressed.length > 0 ? "partial" : "blocked",
|
|
7917
|
+
mode,
|
|
7918
|
+
applied,
|
|
7919
|
+
changed,
|
|
7920
|
+
steps: Object.freeze([...steps]),
|
|
7921
|
+
supportedMethods: ACTION_METHODS,
|
|
7922
|
+
...blockers.length > 0 ? { blockers: Object.freeze(blockers) } : {},
|
|
7923
|
+
...blockerDetails.length > 0 ? { blockerDetails: Object.freeze(blockerDetails) } : {}
|
|
7924
|
+
};
|
|
7925
|
+
}
|
|
7926
|
+
|
|
6135
7927
|
// src/api/v3/_create.ts
|
|
6136
7928
|
function createApiV3(handle, opts) {
|
|
6137
7929
|
const ai = {
|
|
@@ -6147,7 +7939,8 @@ function createApiV3(handle, opts) {
|
|
|
6147
7939
|
...createEvaluateFamily(handle),
|
|
6148
7940
|
...createStatsFamily(handle),
|
|
6149
7941
|
...createOutlineFamily(handle),
|
|
6150
|
-
...createTableActionFamily(handle)
|
|
7942
|
+
...createTableActionFamily(handle),
|
|
7943
|
+
...createActionsFamily(handle)
|
|
6151
7944
|
};
|
|
6152
7945
|
const runtime = {
|
|
6153
7946
|
document: createDocumentFamily(handle),
|