portable-agent-layer 0.64.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/assets/skills/consulting-report/tools/generate-pdf.mjs +2 -2
- package/assets/skills/consulting-report/tools/generate-pdf.ts +5 -2
- package/assets/skills/playwright/SKILL.md +2 -2
- package/assets/skills/playwright/tools/shot.ts +6 -7
- package/assets/skills/projects/SKILL.md +4 -1
- package/assets/templates/settings.claude.json +2 -1
- package/package.json +15 -4
- package/src/cli/index.ts +93 -7
- package/src/cli/migrate.ts +69 -3
- package/src/hooks/lib/anchor.ts +90 -0
- package/src/hooks/lib/bindings.ts +117 -0
- package/src/hooks/lib/export.ts +38 -1
- package/src/hooks/lib/import-merge.ts +220 -0
- package/src/hooks/lib/inference.ts +113 -72
- package/src/hooks/lib/machine.ts +176 -0
- package/src/hooks/lib/projects.ts +223 -15
- package/src/hooks/lib/relationship.ts +3 -1
- package/src/hooks/lib/remote.ts +58 -0
- package/src/hooks/lib/retrieval.ts +8 -2
- package/src/hooks/lib/signals.ts +2 -1
- package/src/hooks/lib/stop.ts +5 -2
- package/src/targets/lib.ts +79 -34
- package/src/tools/agent/algorithm-reflect.ts +45 -11
- package/src/tools/agent/project.ts +148 -23
- package/src/tools/agent/thread.ts +7 -2
- package/assets/skills/playwright/tools/shot-lib.mjs +0 -44
- package/assets/skills/playwright/tools/shot.mjs +0 -89
package/src/targets/lib.ts
CHANGED
|
@@ -50,14 +50,20 @@ export function writeJson(path: string, data: unknown): void {
|
|
|
50
50
|
/** Public PAL repository — the link surfaced in commit/PR co-author credits. */
|
|
51
51
|
export const PAL_REPO_URL = "https://github.com/kovrichard/portable-agent-layer";
|
|
52
52
|
|
|
53
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Build the commit footer (bare URL, autolinks on GitHub) and PR body line
|
|
55
|
+
* (markdown link). `sessionUrl` is off because Claude Code otherwise appends a
|
|
56
|
+
* claude.ai session link to commits made from web or Remote Control sessions,
|
|
57
|
+
* which puts a link to a private transcript in a public history.
|
|
58
|
+
*/
|
|
54
59
|
export function buildAttributionText(
|
|
55
60
|
name: string,
|
|
56
61
|
repoUrl: string = PAL_REPO_URL
|
|
57
|
-
): { commit: string; pr: string } {
|
|
62
|
+
): { commit: string; pr: string; sessionUrl: false } {
|
|
58
63
|
return {
|
|
59
64
|
commit: `Co-authored by ${name} · ${repoUrl}`,
|
|
60
65
|
pr: `Co-authored by [${name}](${repoUrl})`,
|
|
66
|
+
sessionUrl: false,
|
|
61
67
|
};
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -75,7 +81,7 @@ export function applyAttribution(
|
|
|
75
81
|
result.attribution = buildAttributionText(opts.name, opts.repoUrl);
|
|
76
82
|
result.includeCoAuthoredBy = false;
|
|
77
83
|
} else {
|
|
78
|
-
result.attribution = { commit: "", pr: "" };
|
|
84
|
+
result.attribution = { commit: "", pr: "", sessionUrl: false };
|
|
79
85
|
if (result.includeCoAuthoredBy === false) delete result.includeCoAuthoredBy;
|
|
80
86
|
}
|
|
81
87
|
return result;
|
|
@@ -693,8 +699,8 @@ export function scaffoldPalSettings(): void {
|
|
|
693
699
|
|
|
694
700
|
// --- PAL docs (modular context routing files) ---
|
|
695
701
|
|
|
696
|
-
const
|
|
697
|
-
const
|
|
702
|
+
const palDocsDir = () => resolve(palHome(), "docs");
|
|
703
|
+
const palToolsDir = () => resolve(palHome(), "tools");
|
|
698
704
|
|
|
699
705
|
/**
|
|
700
706
|
* Install PAL system docs into ~/.pal/docs/.
|
|
@@ -705,19 +711,19 @@ export function copyPalDocs(): number {
|
|
|
705
711
|
const srcDir = assets.palDocs();
|
|
706
712
|
if (!existsSync(srcDir)) return 0;
|
|
707
713
|
|
|
708
|
-
mkdirSync(
|
|
714
|
+
mkdirSync(palDocsDir(), { recursive: true });
|
|
709
715
|
let count = 0;
|
|
710
716
|
|
|
711
717
|
for (const file of readdirSync(srcDir).filter((f) => f.endsWith(".md"))) {
|
|
712
718
|
const src = resolve(srcDir, file);
|
|
713
|
-
const dst = resolve(
|
|
719
|
+
const dst = resolve(palDocsDir(), file);
|
|
714
720
|
copyFileSync(src, dst);
|
|
715
721
|
count++;
|
|
716
722
|
}
|
|
717
723
|
|
|
718
724
|
// ~/.pal/tools/ → repo agent tools
|
|
719
725
|
const linkType = process.platform === "win32" ? "junction" : "dir";
|
|
720
|
-
ensureSymlink(
|
|
726
|
+
ensureSymlink(palToolsDir(), assets.agentTools(), linkType);
|
|
721
727
|
|
|
722
728
|
return count;
|
|
723
729
|
}
|
|
@@ -726,13 +732,13 @@ export function copyPalDocs(): number {
|
|
|
726
732
|
export function removePalDocs(): void {
|
|
727
733
|
// Remove tools symlink
|
|
728
734
|
try {
|
|
729
|
-
unlinkSync(
|
|
735
|
+
unlinkSync(palToolsDir());
|
|
730
736
|
} catch {
|
|
731
737
|
/* gone */
|
|
732
738
|
}
|
|
733
|
-
if (!existsSync(
|
|
739
|
+
if (!existsSync(palDocsDir())) return;
|
|
734
740
|
try {
|
|
735
|
-
rmSync(
|
|
741
|
+
rmSync(palDocsDir(), { recursive: true });
|
|
736
742
|
log.info("Removed ~/.pal/docs/");
|
|
737
743
|
} catch {
|
|
738
744
|
/* gone */
|
|
@@ -741,7 +747,7 @@ export function removePalDocs(): void {
|
|
|
741
747
|
|
|
742
748
|
// --- Skills ---
|
|
743
749
|
|
|
744
|
-
const
|
|
750
|
+
const palSkillsDir = () => resolve(palHome(), "skills");
|
|
745
751
|
|
|
746
752
|
/**
|
|
747
753
|
* Run one step of a bulk install, naming it only when it fails.
|
|
@@ -772,7 +778,7 @@ export function copySkills(claudeSkillsDir: string): number {
|
|
|
772
778
|
const skillsDir = assets.skills();
|
|
773
779
|
if (!existsSync(skillsDir)) return 0;
|
|
774
780
|
|
|
775
|
-
mkdirSync(
|
|
781
|
+
mkdirSync(palSkillsDir(), { recursive: true });
|
|
776
782
|
mkdirSync(claudeSkillsDir, { recursive: true });
|
|
777
783
|
const linkType = process.platform === "win32" ? "junction" : "dir";
|
|
778
784
|
let count = 0;
|
|
@@ -785,7 +791,7 @@ export function copySkills(claudeSkillsDir: string): number {
|
|
|
785
791
|
const srcDir = resolve(skillsDir, name);
|
|
786
792
|
if (!existsSync(resolve(srcDir, "SKILL.md"))) continue;
|
|
787
793
|
|
|
788
|
-
const palLink = resolve(
|
|
794
|
+
const palLink = resolve(palSkillsDir(), name);
|
|
789
795
|
const claudeLink = resolve(claudeSkillsDir, name);
|
|
790
796
|
const linked = reportOnlyOnFailure(`skill ${name}`, () => {
|
|
791
797
|
// ~/.pal/skills/<name> → <repo>/assets/skills/<name>
|
|
@@ -798,7 +804,7 @@ export function copySkills(claudeSkillsDir: string): number {
|
|
|
798
804
|
|
|
799
805
|
// ~/.agents/skills/ → ~/.pal/skills/
|
|
800
806
|
mkdirSync(platform.agentsDir(), { recursive: true });
|
|
801
|
-
ensureSymlink(resolve(platform.agentsDir(), "skills"),
|
|
807
|
+
ensureSymlink(resolve(platform.agentsDir(), "skills"), palSkillsDir(), linkType);
|
|
802
808
|
|
|
803
809
|
return count;
|
|
804
810
|
}
|
|
@@ -836,8 +842,8 @@ function symlinkPointsInto(link: string, root: string): boolean {
|
|
|
836
842
|
*/
|
|
837
843
|
function pruneStaleSkillLinks(agentSkillsDir: string): string[] {
|
|
838
844
|
const ownedTrees = [
|
|
839
|
-
{ dir:
|
|
840
|
-
{ dir: agentSkillsDir, root:
|
|
845
|
+
{ dir: palSkillsDir(), root: assets.skills() },
|
|
846
|
+
{ dir: agentSkillsDir, root: palSkillsDir() },
|
|
841
847
|
];
|
|
842
848
|
const removed: string[] = [];
|
|
843
849
|
for (const { dir, root } of ownedTrees) {
|
|
@@ -878,7 +884,7 @@ function perSkillAgentDirs(): { agent: string; dir: string }[] {
|
|
|
878
884
|
* is covered by the whole-dir ~/.agents/skills link).
|
|
879
885
|
*/
|
|
880
886
|
export function linkPersonalSkill(name: string): string[] {
|
|
881
|
-
const palLink = resolve(
|
|
887
|
+
const palLink = resolve(palSkillsDir(), name);
|
|
882
888
|
if (!existsSync(resolve(palLink, "SKILL.md"))) {
|
|
883
889
|
throw new Error(`No skill found at ${palLink}/SKILL.md`);
|
|
884
890
|
}
|
|
@@ -892,8 +898,47 @@ export function linkPersonalSkill(name: string): string[] {
|
|
|
892
898
|
return linked;
|
|
893
899
|
}
|
|
894
900
|
|
|
901
|
+
/**
|
|
902
|
+
* The agent config trees PAL writes into when no env override is set. These are
|
|
903
|
+
* the developer's own installed agents, so a test that forgets to point the
|
|
904
|
+
* PAL_*_DIR vars at a sandbox silently rewires their real setup.
|
|
905
|
+
*/
|
|
906
|
+
function realAgentRoots(): string[] {
|
|
907
|
+
const h = homedir();
|
|
908
|
+
return [
|
|
909
|
+
resolve(h, ".pal"),
|
|
910
|
+
resolve(h, ".claude"),
|
|
911
|
+
resolve(h, ".cursor"),
|
|
912
|
+
resolve(h, ".copilot"),
|
|
913
|
+
resolve(h, ".codex"),
|
|
914
|
+
resolve(h, ".agents"),
|
|
915
|
+
resolve(h, ".config", "opencode"),
|
|
916
|
+
];
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Under `bun test` (PAL_TEST_SANDBOX, set by the test preload and inherited by
|
|
921
|
+
* spawned CLIs), refuse any link that would land in a real agent tree. Tests
|
|
922
|
+
* sandbox PAL_HOME far more reliably than they sandbox the per-agent dirs, and
|
|
923
|
+
* the failure is otherwise invisible: the suite passes while the developer's
|
|
924
|
+
* own agents accumulate links into a deleted test directory.
|
|
925
|
+
*/
|
|
926
|
+
function assertInsideTestSandbox(link: string): void {
|
|
927
|
+
if (!process.env.PAL_TEST_SANDBOX) return;
|
|
928
|
+
const escaped = realAgentRoots().find(
|
|
929
|
+
(root) => link === root || link.startsWith(root + sep)
|
|
930
|
+
);
|
|
931
|
+
if (!escaped) return;
|
|
932
|
+
throw new Error(
|
|
933
|
+
`Refusing to write ${link}: outside the test sandbox (${escaped} is a real agent directory). ` +
|
|
934
|
+
"Point PAL_CLAUDE_DIR, PAL_CURSOR_DIR, PAL_COPILOT_DIR, PAL_CODEX_DIR, " +
|
|
935
|
+
"PAL_OPENCODE_DIR and PAL_AGENTS_DIR at a temp directory in this test."
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
|
|
895
939
|
/** Create or update a symlink/junction, replacing any non-symlink entry. */
|
|
896
940
|
function ensureSymlink(link: string, target: string, type: "dir" | "junction"): void {
|
|
941
|
+
assertInsideTestSandbox(link);
|
|
897
942
|
try {
|
|
898
943
|
const st = lstatSync(link);
|
|
899
944
|
if (st.isSymbolicLink()) return; // already a symlink, leave it
|
|
@@ -941,7 +986,7 @@ export function removeSkills(claudeSkillsDir: string): string[] {
|
|
|
941
986
|
for (const name of readdirSync(skillsDir)) {
|
|
942
987
|
if (!existsSync(resolve(skillsDir, name, "SKILL.md"))) continue;
|
|
943
988
|
|
|
944
|
-
for (const link of [resolve(
|
|
989
|
+
for (const link of [resolve(palSkillsDir(), name), resolve(claudeSkillsDir, name)]) {
|
|
945
990
|
try {
|
|
946
991
|
unlinkSync(link);
|
|
947
992
|
} catch {
|
|
@@ -964,14 +1009,14 @@ export function removeSkills(claudeSkillsDir: string): string[] {
|
|
|
964
1009
|
|
|
965
1010
|
// --- Agents ---
|
|
966
1011
|
|
|
967
|
-
const
|
|
1012
|
+
const claudeAgentsDir = () => resolve(platform.claudeDir(), "agents");
|
|
968
1013
|
|
|
969
1014
|
/**
|
|
970
1015
|
* Install PAL agent definitions into ~/.claude/agents/.
|
|
971
1016
|
* Always overwrites — engine-managed, not user-editable.
|
|
972
1017
|
*/
|
|
973
1018
|
export function copyAgents(): number {
|
|
974
|
-
return installAgents(
|
|
1019
|
+
return installAgents(claudeAgentsDir(), "claude");
|
|
975
1020
|
}
|
|
976
1021
|
|
|
977
1022
|
/** Remove PAL agents from ~/.claude/agents/ */
|
|
@@ -981,7 +1026,7 @@ export function removeAgents(): string[] {
|
|
|
981
1026
|
|
|
982
1027
|
const removed: string[] = [];
|
|
983
1028
|
for (const file of readdirSync(agentsDir).filter((f) => f.endsWith(".md"))) {
|
|
984
|
-
const dst = resolve(
|
|
1029
|
+
const dst = resolve(claudeAgentsDir(), file);
|
|
985
1030
|
if (existsSync(dst)) {
|
|
986
1031
|
unlinkSync(dst);
|
|
987
1032
|
const name = file.replace(/\.md$/, "");
|
|
@@ -994,9 +1039,9 @@ export function removeAgents(): string[] {
|
|
|
994
1039
|
|
|
995
1040
|
/** Count agent .md files in ~/.claude/agents/ */
|
|
996
1041
|
export function countAgents(): number {
|
|
997
|
-
if (!existsSync(
|
|
1042
|
+
if (!existsSync(claudeAgentsDir())) return 0;
|
|
998
1043
|
try {
|
|
999
|
-
return readdirSync(
|
|
1044
|
+
return readdirSync(claudeAgentsDir()).filter((f) => f.endsWith(".md")).length;
|
|
1000
1045
|
} catch {
|
|
1001
1046
|
return 0;
|
|
1002
1047
|
}
|
|
@@ -1132,7 +1177,7 @@ export function removeAgentsFromCopilot(copilotAgentsDir: string): string[] {
|
|
|
1132
1177
|
* Store for user-authored subagents: ~/.pal/agents/<name>.md — one merged
|
|
1133
1178
|
* multi-platform frontmatter file per subagent (same schema as assets/agents/).
|
|
1134
1179
|
*/
|
|
1135
|
-
const
|
|
1180
|
+
const palAgentsStore = () => resolve(palHome(), "agents");
|
|
1136
1181
|
|
|
1137
1182
|
/**
|
|
1138
1183
|
* Each installed agent and the native agents directory a personal subagent is
|
|
@@ -1161,8 +1206,8 @@ function shippedAgentNames(): Set<string> {
|
|
|
1161
1206
|
|
|
1162
1207
|
/** List the user-authored subagents in ~/.pal/agents/. */
|
|
1163
1208
|
export function listPersonalSubagents(): string[] {
|
|
1164
|
-
if (!existsSync(
|
|
1165
|
-
return readdirSync(
|
|
1209
|
+
if (!existsSync(palAgentsStore())) return [];
|
|
1210
|
+
return readdirSync(palAgentsStore())
|
|
1166
1211
|
.filter((f) => f.endsWith(".md"))
|
|
1167
1212
|
.map((f) => f.replace(/\.md$/, ""))
|
|
1168
1213
|
.sort();
|
|
@@ -1176,7 +1221,7 @@ export function listPersonalSubagents(): string[] {
|
|
|
1176
1221
|
* its own frontmatter shape. Returns the agents it was installed into.
|
|
1177
1222
|
*/
|
|
1178
1223
|
export function installPersonalSubagent(name: string): string[] {
|
|
1179
|
-
const src = resolve(
|
|
1224
|
+
const src = resolve(palAgentsStore(), `${name}.md`);
|
|
1180
1225
|
if (!existsSync(src)) {
|
|
1181
1226
|
throw new Error(`No subagent found at ${src}`);
|
|
1182
1227
|
}
|
|
@@ -1408,7 +1453,7 @@ function extractTriggers(description: string): string[] {
|
|
|
1408
1453
|
* Called during install after skills are symlinked.
|
|
1409
1454
|
*/
|
|
1410
1455
|
export function generateSkillIndex(): number {
|
|
1411
|
-
if (!existsSync(
|
|
1456
|
+
if (!existsSync(palSkillsDir())) return 0;
|
|
1412
1457
|
|
|
1413
1458
|
const index: SkillIndex = {
|
|
1414
1459
|
generated: new Date().toISOString(),
|
|
@@ -1416,8 +1461,8 @@ export function generateSkillIndex(): number {
|
|
|
1416
1461
|
skills: {},
|
|
1417
1462
|
};
|
|
1418
1463
|
|
|
1419
|
-
for (const name of readdirSync(
|
|
1420
|
-
const skillMd = resolve(
|
|
1464
|
+
for (const name of readdirSync(palSkillsDir())) {
|
|
1465
|
+
const skillMd = resolve(palSkillsDir(), name, "SKILL.md");
|
|
1421
1466
|
if (!existsSync(skillMd)) continue;
|
|
1422
1467
|
|
|
1423
1468
|
try {
|
|
@@ -1456,10 +1501,10 @@ export function generateSkillIndex(): number {
|
|
|
1456
1501
|
|
|
1457
1502
|
/** Count skill subdirectories in ~/.pal/skills/ */
|
|
1458
1503
|
export function countSkills(): number {
|
|
1459
|
-
if (!existsSync(
|
|
1504
|
+
if (!existsSync(palSkillsDir())) return 0;
|
|
1460
1505
|
try {
|
|
1461
|
-
return readdirSync(
|
|
1462
|
-
existsSync(resolve(
|
|
1506
|
+
return readdirSync(palSkillsDir()).filter((f) =>
|
|
1507
|
+
existsSync(resolve(palSkillsDir(), f, "SKILL.md"))
|
|
1463
1508
|
).length;
|
|
1464
1509
|
} catch {
|
|
1465
1510
|
return 0;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
4
|
* AlgorithmReflect — Append structured algorithm reflections to JSONL.
|
|
4
5
|
*
|
|
@@ -14,6 +15,8 @@
|
|
|
14
15
|
|
|
15
16
|
import { appendFileSync } from "node:fs";
|
|
16
17
|
import { parseArgs } from "node:util";
|
|
18
|
+
import { encodeAnchor } from "../../hooks/lib/anchor";
|
|
19
|
+
import { loadMachine } from "../../hooks/lib/machine";
|
|
17
20
|
import { paths } from "../../hooks/lib/paths";
|
|
18
21
|
import { emit } from "../lib/emit";
|
|
19
22
|
|
|
@@ -22,6 +25,7 @@ import { emit } from "../lib/emit";
|
|
|
22
25
|
interface AlgorithmReflection {
|
|
23
26
|
timestamp: string;
|
|
24
27
|
cwd: string;
|
|
28
|
+
m: string;
|
|
25
29
|
task: string;
|
|
26
30
|
criteria_count: number;
|
|
27
31
|
criteria_passed: number;
|
|
@@ -41,6 +45,40 @@ function reflectionsPath(): string {
|
|
|
41
45
|
return paths.reflectionsFile();
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Assemble a reflection record from CLI-style input, stamping the current
|
|
50
|
+
* cwd (anchored) and this machine's id. Exported so the stamping logic is
|
|
51
|
+
* directly testable without going through argv parsing.
|
|
52
|
+
*/
|
|
53
|
+
export function buildReflection(input: {
|
|
54
|
+
task: string;
|
|
55
|
+
q1: string;
|
|
56
|
+
q2: string;
|
|
57
|
+
q3: string;
|
|
58
|
+
criteria_count?: number;
|
|
59
|
+
criteria_passed?: number;
|
|
60
|
+
criteria_failed?: number;
|
|
61
|
+
sentiment?: number;
|
|
62
|
+
scope?: string;
|
|
63
|
+
}): AlgorithmReflection {
|
|
64
|
+
return {
|
|
65
|
+
timestamp: new Date().toISOString(),
|
|
66
|
+
cwd: encodeAnchor(process.cwd()),
|
|
67
|
+
m: loadMachine().id,
|
|
68
|
+
task: input.task,
|
|
69
|
+
criteria_count: input.criteria_count ?? 0,
|
|
70
|
+
criteria_passed: input.criteria_passed ?? 0,
|
|
71
|
+
criteria_failed: input.criteria_failed ?? 0,
|
|
72
|
+
sentiment: Math.max(1, Math.min(10, input.sentiment ?? 5)),
|
|
73
|
+
q1: input.q1,
|
|
74
|
+
q2: input.q2,
|
|
75
|
+
q3: input.q3,
|
|
76
|
+
// Default to general (the ~94% case); only "task-specific" suppresses it
|
|
77
|
+
// from algorithm-update clustering.
|
|
78
|
+
scope: input.scope === "task-specific" ? "task-specific" : "general",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
44
82
|
function appendReflection(reflection: AlgorithmReflection): {
|
|
45
83
|
success: boolean;
|
|
46
84
|
message: string;
|
|
@@ -105,21 +143,17 @@ Output: algorithm-reflections.jsonl in memory/learning/reflections/
|
|
|
105
143
|
process.exit(1);
|
|
106
144
|
}
|
|
107
145
|
|
|
108
|
-
const reflection
|
|
109
|
-
timestamp: new Date().toISOString(),
|
|
110
|
-
cwd: process.cwd(),
|
|
146
|
+
const reflection = buildReflection({
|
|
111
147
|
task: values.task,
|
|
112
|
-
criteria_count: parseInt(values.criteria || "0", 10),
|
|
113
|
-
criteria_passed: parseInt(values.passed || "0", 10),
|
|
114
|
-
criteria_failed: parseInt(values.failed || "0", 10),
|
|
115
|
-
sentiment: Math.max(1, Math.min(10, parseInt(values.sentiment || "5", 10))),
|
|
116
148
|
q1: values.q1,
|
|
117
149
|
q2: values.q2,
|
|
118
150
|
q3: values.q3,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
151
|
+
criteria_count: parseInt(values.criteria || "0", 10),
|
|
152
|
+
criteria_passed: parseInt(values.passed || "0", 10),
|
|
153
|
+
criteria_failed: parseInt(values.failed || "0", 10),
|
|
154
|
+
sentiment: parseInt(values.sentiment || "5", 10),
|
|
155
|
+
scope: values.scope,
|
|
156
|
+
});
|
|
123
157
|
|
|
124
158
|
const result = appendReflection(reflection);
|
|
125
159
|
emit.ok(result.message);
|