billion-context-omp 0.2.2 → 0.2.3
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/README.zh-CN.md +1 -1
- package/dist/decompress-tool.d.ts +1 -0
- package/dist/index.js +82 -26
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ omp's built-in `/compact` is intercepted and replaced by an ACP model-summarized
|
|
|
77
77
|
|------|-------------|
|
|
78
78
|
| `compress` | Replace a contiguous message range with a detailed summary |
|
|
79
79
|
| `decompress` | Restore a previously compressed block's content (to file by default; `inline:true` for single messages) |
|
|
80
|
-
| `search_context` | Search compressed block summaries
|
|
80
|
+
| `search_context` | Search compressed block summaries and the original messages folded into them by keyword (visible messages are not indexed) |
|
|
81
81
|
| `acp_status` | Show context usage, compressed blocks, compressible ranges |
|
|
82
82
|
|
|
83
83
|
> The `acp_delegate` sub-agent subsystem from the Pi build is intentionally **not** registered — omp ships its own multi-agent orchestration, and duplicate delegation tools would conflict.
|
package/README.zh-CN.md
CHANGED
|
@@ -77,7 +77,7 @@ omp 内置的 `/compact` 被拦截,替换为 ACP 模型摘要式 compaction,
|
|
|
77
77
|
|------|------|
|
|
78
78
|
| `compress` | 把一段连续消息区间替换为详细摘要 |
|
|
79
79
|
| `decompress` | 恢复之前压缩的块内容(默认写文件;单条消息可 `inline:true`) |
|
|
80
|
-
| `search_context` |
|
|
80
|
+
| `search_context` | 按关键字搜索压缩块摘要及被折叠其中的原始消息(可见消息不建索引) |
|
|
81
81
|
| `acp_status` | 显示上下文用量、压缩块、可压缩区间 |
|
|
82
82
|
|
|
83
83
|
> Pi 版中的 `acp_delegate` 子代理系统在此**有意不注册**——omp 自带多代理编排,重复的委派工具会冲突。
|
|
@@ -12,4 +12,5 @@ declare const DecompressParams: import("@oh-my-pi/omptype").FluentType<{
|
|
|
12
12
|
toFile?: string | undefined;
|
|
13
13
|
}>;
|
|
14
14
|
export declare function makeDecompressTool(runtime: AcpRuntime): ToolDefinition<typeof DecompressParams>;
|
|
15
|
+
export declare function pruneAutoFiles(): void;
|
|
15
16
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -2769,8 +2769,12 @@ function resolveConfig(adapter, liveContextLimit) {
|
|
|
2769
2769
|
return config;
|
|
2770
2770
|
}
|
|
2771
2771
|
function parsePercent(v) {
|
|
2772
|
-
const
|
|
2773
|
-
if (!Number.isFinite(
|
|
2772
|
+
const raw = typeof v === "number" ? v : v.trim().endsWith("%") ? Number(v.trim().slice(0, -1)) : Number(v);
|
|
2773
|
+
if (!Number.isFinite(raw)) return 0;
|
|
2774
|
+
const n = raw > 1 ? raw / 100 : raw;
|
|
2775
|
+
if (n > 1 || n < 0) {
|
|
2776
|
+
logWarn("config", { event: "percent-clamped", value: String(v), clamped: Math.min(1, Math.max(0, n)) });
|
|
2777
|
+
}
|
|
2774
2778
|
return Math.min(1, Math.max(0, n));
|
|
2775
2779
|
}
|
|
2776
2780
|
|
|
@@ -3159,7 +3163,7 @@ function rangeFingerprints(ranges, coreMessages, byRef, blocks) {
|
|
|
3159
3163
|
|
|
3160
3164
|
// src/runtime.ts
|
|
3161
3165
|
function freshSlot(preserveFrom) {
|
|
3162
|
-
const slot = { identities: [], foldedLen: 0, preview: false, state: createInitialState(), coreMessages: [], appliedCallIds: /* @__PURE__ */ new Set(), rejectStreak: 0 };
|
|
3166
|
+
const slot = { identities: [], foldedLen: 0, preview: false, state: createInitialState(), coreMessages: [], appliedCallIds: /* @__PURE__ */ new Set(), rejectStreak: 0, lastRebuiltOutput: null };
|
|
3163
3167
|
if (preserveFrom) {
|
|
3164
3168
|
slot.state = { ...slot.state, nudge: preserveFrom.state.nudge };
|
|
3165
3169
|
slot.rejectStreak = preserveFrom.rejectStreak;
|
|
@@ -3223,6 +3227,17 @@ function createRuntime(adapter) {
|
|
|
3223
3227
|
slots.set(sid, slot);
|
|
3224
3228
|
}
|
|
3225
3229
|
const ids = stream.map(messageIdentity);
|
|
3230
|
+
const lastOut = slot.lastRebuiltOutput;
|
|
3231
|
+
if (lastOut !== null && ids.length === lastOut.length && ids.every((id, i) => id === lastOut[i])) {
|
|
3232
|
+
const coreMessages2 = streamToCoreMessages(stream);
|
|
3233
|
+
const originalById2 = /* @__PURE__ */ new Map();
|
|
3234
|
+
stream.forEach((message, i) => originalById2.set(`p${i + 1}`, message));
|
|
3235
|
+
slot.identities = ids;
|
|
3236
|
+
slot.foldedLen = ids.length;
|
|
3237
|
+
slot.coreMessages = coreMessages2;
|
|
3238
|
+
debug.event("feedback-reuse", { sid, msgs: ids.length, blocks: slot.state.blocks.length });
|
|
3239
|
+
return { state: slot.state, coreMessages: coreMessages2, originalById: originalById2, streamLen: ids.length };
|
|
3240
|
+
}
|
|
3226
3241
|
let lcp = 0;
|
|
3227
3242
|
while (lcp < Math.min(ids.length, slot.identities.length) && ids[lcp] === slot.identities[lcp]) lcp++;
|
|
3228
3243
|
if (lcp < slot.foldedLen) {
|
|
@@ -3311,6 +3326,10 @@ function createRuntime(adapter) {
|
|
|
3311
3326
|
slot.state = state;
|
|
3312
3327
|
if (toolCallId) slot.appliedCallIds.add(toolCallId);
|
|
3313
3328
|
}
|
|
3329
|
+
function recordRebuiltOutput(ctx, rebuilt) {
|
|
3330
|
+
const slot = slotFor(sidOf(ctx));
|
|
3331
|
+
slot.lastRebuiltOutput = rebuilt.map(messageIdentity);
|
|
3332
|
+
}
|
|
3314
3333
|
function noteCompressOutcome(ctx, ok) {
|
|
3315
3334
|
const slot = slotFor(sidOf(ctx));
|
|
3316
3335
|
slot.rejectStreak = ok ? 0 : slot.rejectStreak + 1;
|
|
@@ -3335,6 +3354,7 @@ function createRuntime(adapter) {
|
|
|
3335
3354
|
foldStream,
|
|
3336
3355
|
stateFor,
|
|
3337
3356
|
commitFoldState,
|
|
3357
|
+
recordRebuiltOutput,
|
|
3338
3358
|
noteCompressOutcome,
|
|
3339
3359
|
forgetSession,
|
|
3340
3360
|
primeFold,
|
|
@@ -3435,11 +3455,13 @@ function makeCompressTool(runtime) {
|
|
|
3435
3455
|
}
|
|
3436
3456
|
async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
3437
3457
|
const ranges = args.content ?? [];
|
|
3438
|
-
if (ranges.length === 0)
|
|
3458
|
+
if (ranges.length === 0) {
|
|
3459
|
+
return rejectionMessage(ctx, runtime, "No ranges provided. No changes applied \u2014 send content: [{ startId, endId, summary }].");
|
|
3460
|
+
}
|
|
3439
3461
|
const invalid = ranges.filter((r) => !r || typeof r.summary !== "string" || !r.summary.trim() || !r.startId || !r.endId);
|
|
3440
3462
|
if (invalid.length > 0) {
|
|
3441
3463
|
logWarn("compress", { sid: ctx.sessionManager.getSessionId(), event: "invalid-ranges", count: invalid.length });
|
|
3442
|
-
return `Every range needs startId, endId and a summary (min 50 chars) \u2014 got ${invalid.length} range(s) missing fields. Re-send the FULL ranges array with summaries included
|
|
3464
|
+
return rejectionMessage(ctx, runtime, `Every range needs startId, endId and a summary (min 50 chars) \u2014 got ${invalid.length} range(s) missing fields. Re-send the FULL ranges array with summaries included. No changes applied \u2014 run acp_status for current refs.`);
|
|
3443
3465
|
}
|
|
3444
3466
|
const releaseLock = await runtime.acquireLock(ctx.sessionManager.getSessionId());
|
|
3445
3467
|
try {
|
|
@@ -3550,10 +3572,12 @@ STOP: ${streak} compress calls rejected in a row. Do NOT retry the same range. R
|
|
|
3550
3572
|
// src/decompress-tool.ts
|
|
3551
3573
|
import { type as type2 } from "@oh-my-pi/omptype";
|
|
3552
3574
|
import { writeFile, mkdir } from "fs/promises";
|
|
3553
|
-
import { realpathSync } from "fs";
|
|
3575
|
+
import { realpathSync, readdirSync, statSync as statSync2, unlinkSync } from "fs";
|
|
3554
3576
|
import { resolve, relative, isAbsolute, join as join2, dirname as dirname2, basename } from "path";
|
|
3555
3577
|
import { tmpdir } from "os";
|
|
3556
|
-
|
|
3578
|
+
function autoDir() {
|
|
3579
|
+
return join2(homeDir() || tmpdir(), ".cache", "omp", "acp-decompress");
|
|
3580
|
+
}
|
|
3557
3581
|
var PREVIEW_CHARS = 600;
|
|
3558
3582
|
var MESSAGE_INLINE_THRESHOLD = 2e3;
|
|
3559
3583
|
var DecompressParams = type2({
|
|
@@ -3618,7 +3642,31 @@ function resolveToFilePath(targetPath) {
|
|
|
3618
3642
|
return resolved;
|
|
3619
3643
|
}
|
|
3620
3644
|
function autoFilePath(blockId) {
|
|
3621
|
-
return join2(
|
|
3645
|
+
return join2(autoDir(), `${blockId}-${Date.now()}.txt`);
|
|
3646
|
+
}
|
|
3647
|
+
var MAX_AUTO_FILES = 100;
|
|
3648
|
+
function pruneAutoFiles() {
|
|
3649
|
+
const dir = autoDir();
|
|
3650
|
+
try {
|
|
3651
|
+
const files = readdirSync(dir).map((f) => {
|
|
3652
|
+
try {
|
|
3653
|
+
return { f, m: statSync2(join2(dir, f)).mtimeMs };
|
|
3654
|
+
} catch {
|
|
3655
|
+
return null;
|
|
3656
|
+
}
|
|
3657
|
+
}).filter((x) => x !== null);
|
|
3658
|
+
if (files.length <= MAX_AUTO_FILES) return;
|
|
3659
|
+
files.sort((a, b) => a.m - b.m);
|
|
3660
|
+
const excess = files.slice(0, files.length - MAX_AUTO_FILES);
|
|
3661
|
+
for (const { f } of excess) {
|
|
3662
|
+
try {
|
|
3663
|
+
unlinkSync(join2(dir, f));
|
|
3664
|
+
} catch {
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
debug.event("decompress-pruned", { dir, removed: excess.length });
|
|
3668
|
+
} catch {
|
|
3669
|
+
}
|
|
3622
3670
|
}
|
|
3623
3671
|
function headPreview(text) {
|
|
3624
3672
|
if (text.length <= PREVIEW_CHARS) return text;
|
|
@@ -3652,8 +3700,9 @@ ${text}`;
|
|
|
3652
3700
|
logError("decompress", { sid: ctx.sessionManager.getSessionId(), event: "message-path-rejected", ref, toFile: args.toFile });
|
|
3653
3701
|
return targetPath.error;
|
|
3654
3702
|
}
|
|
3655
|
-
await mkdir(
|
|
3703
|
+
await mkdir(autoDir(), { recursive: true }).catch((e) => logError("decompress", { event: "mkdir-failed", dir: autoDir(), error: e instanceof Error ? e.message : String(e) }));
|
|
3656
3704
|
await writeFile(targetPath, text, "utf8");
|
|
3705
|
+
if (!args.toFile) pruneAutoFiles();
|
|
3657
3706
|
debug.event("decompress-message", { ref, ownerBlockId, mode: "file", path: targetPath, chars: text.length });
|
|
3658
3707
|
logInfo("decompress", { sid: ctx.sessionManager.getSessionId(), event: "message", mode: "file", ref, ownerBlockId, path: targetPath, chars: text.length });
|
|
3659
3708
|
return [
|
|
@@ -3695,8 +3744,9 @@ ${text}`;
|
|
|
3695
3744
|
logError("decompress", { sid: ctx.sessionManager.getSessionId(), event: "block-path-rejected", blockId, toFile: args.toFile });
|
|
3696
3745
|
return targetPath.error;
|
|
3697
3746
|
}
|
|
3698
|
-
await mkdir(
|
|
3747
|
+
await mkdir(autoDir(), { recursive: true }).catch((e) => logError("decompress", { event: "mkdir-failed", dir: autoDir(), error: e instanceof Error ? e.message : String(e) }));
|
|
3699
3748
|
await writeFile(targetPath, text, "utf8");
|
|
3749
|
+
if (!args.toFile) pruneAutoFiles();
|
|
3700
3750
|
debug.event("decompress", { blockId, full, count, mode: "file", path: targetPath, chars: text.length });
|
|
3701
3751
|
logInfo("decompress", { sid: ctx.sessionManager.getSessionId(), event: "block", mode: "file", blockId, full, count, path: targetPath, chars: text.length });
|
|
3702
3752
|
const itemWord = count === 1 ? "item" : "items";
|
|
@@ -4508,7 +4558,7 @@ async function statusReport(runtime, ctx) {
|
|
|
4508
4558
|
const coveredIds = collectCoveredMessageIds(state);
|
|
4509
4559
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
4510
4560
|
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
|
|
4511
|
-
const versionStr = "0.2.
|
|
4561
|
+
const versionStr = "0.2.3" ? `billion-context-omp@${"0.2.3"}` : void 0;
|
|
4512
4562
|
return buildStatusPanel({
|
|
4513
4563
|
version: versionStr,
|
|
4514
4564
|
tokenCount: sessionTokens,
|
|
@@ -4893,7 +4943,7 @@ You have four context-management tools:
|
|
|
4893
4943
|
|
|
4894
4944
|
- compress \u2014 Replace a contiguous range of older conversation with a single detailed summary you write. Use when content is genuinely consumed (no longer needed for the current task step). Single range: compress({ content: [{ topic: "Session Opener", startId: "m00150", endId: "m00220", summary: "..." }] }) \u2014 topic is recommended but optional. Batch (multiple unrelated ranges, each with its own topic): compress({ content: [{ topic: "Auth", startId: "m00150", endId: "m00220", summary: "..." }, { topic: "Deploy", startId: "m00300", endId: "m00350", summary: "..." }] }). Call it as a normal tool \u2014 summaries are plain string arguments.
|
|
4895
4945
|
- decompress \u2014 Restore a previously compressed block's content. The block stays compressed \u2014 context and cache prefix are not disrupted. By DEFAULT content is written to an auto-generated file (avoids context bloat); use the read tool to view it. Pass inline:true to return content in the tool result instead (appends to context). full:true recurses to original messages. Example: decompress({ blockId: "b5" }) or decompress({ blockId: "b5", full: true }) or decompress({ blockId: "b5", inline: true }).
|
|
4896
|
-
- search_context \u2014 Search compressed block summaries
|
|
4946
|
+
- search_context \u2014 Search compressed block summaries AND the original messages folded into them by keyword (messages still visible in context are not indexed \u2014 you can already see them). Use BEFORE decompressing to find the right block. Example: search_context({ query: "auth token refresh" }).
|
|
4897
4947
|
- acp_status \u2014 Context status with compressible ranges. No args = overview + totals. scope:"uncompressed" for range view; add view:"messages" for per-message listing. scope:"compressed" for block details.
|
|
4898
4948
|
|
|
4899
4949
|
${prompts.compressPhilosophy}
|
|
@@ -5216,7 +5266,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
5216
5266
|
const data = await res.json();
|
|
5217
5267
|
const latest = data.version;
|
|
5218
5268
|
if (!latest) return;
|
|
5219
|
-
const current = runtimeVersion ?? "0.2.
|
|
5269
|
+
const current = runtimeVersion ?? "0.2.3";
|
|
5220
5270
|
const hasUpdate = isNewer(latest, current);
|
|
5221
5271
|
debug.event("update-check", {
|
|
5222
5272
|
current,
|
|
@@ -5251,20 +5301,20 @@ async function getRuntimeVersion() {
|
|
|
5251
5301
|
}
|
|
5252
5302
|
|
|
5253
5303
|
// src/dump.ts
|
|
5254
|
-
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readdirSync, unlinkSync } from "fs";
|
|
5304
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
5255
5305
|
import * as path2 from "path";
|
|
5256
5306
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME4 } from "@oh-my-pi/pi-utils";
|
|
5257
5307
|
var counters = {};
|
|
5258
5308
|
var MAX_FILES_PER_PREFIX = 200;
|
|
5259
5309
|
function pruneDumps(dir, prefixTest, seqOf) {
|
|
5260
5310
|
try {
|
|
5261
|
-
const files =
|
|
5311
|
+
const files = readdirSync2(dir).filter(prefixTest).map((f) => ({ f, n: seqOf(f) })).filter((x) => !Number.isNaN(x.n));
|
|
5262
5312
|
if (files.length <= MAX_FILES_PER_PREFIX) return;
|
|
5263
5313
|
files.sort((a, b) => a.n - b.n);
|
|
5264
5314
|
const excess = files.slice(0, files.length - MAX_FILES_PER_PREFIX);
|
|
5265
5315
|
for (const { f } of excess) {
|
|
5266
5316
|
try {
|
|
5267
|
-
|
|
5317
|
+
unlinkSync2(path2.join(dir, f));
|
|
5268
5318
|
} catch {
|
|
5269
5319
|
}
|
|
5270
5320
|
}
|
|
@@ -5282,7 +5332,7 @@ function dumpContextMessages(messages, meta) {
|
|
|
5282
5332
|
mkdirSync2(dir, { recursive: true });
|
|
5283
5333
|
if (!(dir in counters)) {
|
|
5284
5334
|
try {
|
|
5285
|
-
const existing =
|
|
5335
|
+
const existing = readdirSync2(dir).filter((f) => /^\d{4}\.json$/.test(f));
|
|
5286
5336
|
const max = existing.reduce((mx, f) => {
|
|
5287
5337
|
const n = parseInt(f, 10);
|
|
5288
5338
|
return Number.isNaN(n) ? mx : Math.max(mx, n);
|
|
@@ -5365,7 +5415,7 @@ function dumpProviderRequest(payload, meta) {
|
|
|
5365
5415
|
mkdirSync2(dir, { recursive: true });
|
|
5366
5416
|
if (!("req" in counters)) {
|
|
5367
5417
|
try {
|
|
5368
|
-
const existing =
|
|
5418
|
+
const existing = readdirSync2(dir).filter((f) => /^req_\d+\.json$/.test(f));
|
|
5369
5419
|
const max = existing.reduce((mx, f) => {
|
|
5370
5420
|
const n = parseInt(f.slice(4, -5), 10);
|
|
5371
5421
|
return Number.isNaN(n) ? mx : Math.max(mx, n);
|
|
@@ -5404,10 +5454,11 @@ import * as path3 from "path";
|
|
|
5404
5454
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME5 } from "@oh-my-pi/pi-utils";
|
|
5405
5455
|
async function loadUserConfig(cwd) {
|
|
5406
5456
|
const home = homeDir();
|
|
5457
|
+
const globalDir = join8(home, CONFIG_DIR_NAME5);
|
|
5407
5458
|
const merged = {};
|
|
5408
|
-
for (const base of [
|
|
5459
|
+
for (const base of [globalDir, join8(cwd, CONFIG_DIR_NAME5)]) {
|
|
5409
5460
|
const file = join8(base, "acp-omp.json");
|
|
5410
|
-
const allowPrompts = base
|
|
5461
|
+
const allowPrompts = base === globalDir;
|
|
5411
5462
|
try {
|
|
5412
5463
|
const raw = await fs.readFile(file, "utf8");
|
|
5413
5464
|
const parsed = JSON.parse(raw);
|
|
@@ -5532,9 +5583,9 @@ function wireCompactionDisable(pi, runtime) {
|
|
|
5532
5583
|
function wireSessionLifecycle(pi, runtime) {
|
|
5533
5584
|
pi.on("session_start", async (_event, ctx) => {
|
|
5534
5585
|
const sid = ctx.sessionManager.getSessionId();
|
|
5535
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.
|
|
5586
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.3" : null });
|
|
5536
5587
|
const selfPath = import.meta.url;
|
|
5537
|
-
const conflict = stampAndDetect(selfPath, true ? "0.2.
|
|
5588
|
+
const conflict = stampAndDetect(selfPath, true ? "0.2.3" : null);
|
|
5538
5589
|
if (conflict) {
|
|
5539
5590
|
logWarn("instance", { event: "dual-instance", self: selfPath, other: conflict.path, otherPid: conflict.pid, otherVersion: conflict.version });
|
|
5540
5591
|
try {
|
|
@@ -5573,6 +5624,7 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
5573
5624
|
async function transformStream(ctx, runtime, input, mode) {
|
|
5574
5625
|
const sid = ctx.sessionManager.getSessionId();
|
|
5575
5626
|
const release = await runtime.acquireLock(sid);
|
|
5627
|
+
let result;
|
|
5576
5628
|
try {
|
|
5577
5629
|
if (input.length === 0) {
|
|
5578
5630
|
debug.event("empty-stream-bypass", { sid });
|
|
@@ -5678,21 +5730,25 @@ ${rendered.text}${example}`);
|
|
|
5678
5730
|
}
|
|
5679
5731
|
}
|
|
5680
5732
|
}
|
|
5733
|
+
if (mode === "context") {
|
|
5734
|
+
runtime.recordRebuiltOutput(ctx, rebuilt);
|
|
5735
|
+
}
|
|
5681
5736
|
dumpContextMessages(rebuilt, {
|
|
5682
5737
|
sid,
|
|
5683
5738
|
injected: nudgeInjected,
|
|
5684
5739
|
emergency: turn.nudge?.breakdown?.emergencyOverride === 1
|
|
5685
5740
|
});
|
|
5686
|
-
|
|
5687
|
-
if (ctx.hasUI) ctx.ui.notify(msg);
|
|
5688
|
-
});
|
|
5689
|
-
return { rebuilt, nudgeInjected };
|
|
5741
|
+
result = { rebuilt, nudgeInjected };
|
|
5690
5742
|
} catch (e) {
|
|
5691
5743
|
logThrow("context", e, { sid, phase: "transform", mode });
|
|
5692
5744
|
throw e;
|
|
5693
5745
|
} finally {
|
|
5694
5746
|
release();
|
|
5695
5747
|
}
|
|
5748
|
+
await checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
|
|
5749
|
+
if (ctx.hasUI) ctx.ui.notify(msg);
|
|
5750
|
+
});
|
|
5751
|
+
return result;
|
|
5696
5752
|
}
|
|
5697
5753
|
function wireContextTransform(pi, runtime) {
|
|
5698
5754
|
pi.on("context", async (event, ctx) => {
|