@plurnk/plurnk-service 0.8.0 → 0.10.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/SPEC.md +44 -56
- package/dist/content/line-marker.d.ts +2 -17
- package/dist/content/line-marker.d.ts.map +1 -1
- package/dist/content/line-marker.js +11 -319
- package/dist/content/line-marker.js.map +1 -1
- package/dist/content/matcher.d.ts.map +1 -1
- package/dist/content/matcher.js +9 -33
- package/dist/content/matcher.js.map +1 -1
- package/dist/content/mimetype-binary.d.ts +0 -1
- package/dist/content/mimetype-binary.d.ts.map +1 -1
- package/dist/content/mimetype-binary.js +13 -82
- package/dist/content/mimetype-binary.js.map +1 -1
- package/dist/content/path-mimetype.d.ts +0 -1
- package/dist/content/path-mimetype.d.ts.map +1 -1
- package/dist/content/path-mimetype.js +10 -44
- package/dist/content/path-mimetype.js.map +1 -1
- package/dist/core/ChannelWrite.d.ts +2 -0
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +8 -2
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/Engine.d.ts +2 -0
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +72 -109
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/ExecutorRegistry.d.ts +26 -0
- package/dist/core/ExecutorRegistry.d.ts.map +1 -0
- package/dist/core/ExecutorRegistry.js +99 -0
- package/dist/core/ExecutorRegistry.js.map +1 -0
- package/dist/core/ProviderInstantiate.d.ts.map +1 -1
- package/dist/core/ProviderInstantiate.js +15 -1
- package/dist/core/ProviderInstantiate.js.map +1 -1
- package/dist/core/git-membership.js +7 -7
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/packet-wire.d.ts +0 -5
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +2 -54
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/resolveForLoop.d.ts +0 -9
- package/dist/core/resolveForLoop.d.ts.map +1 -1
- package/dist/core/resolveForLoop.js +7 -29
- package/dist/core/resolveForLoop.js.map +1 -1
- package/dist/core/results.d.ts +6 -34
- package/dist/core/results.d.ts.map +1 -1
- package/dist/core/results.js +19 -44
- package/dist/core/results.js.map +1 -1
- package/dist/core/scheme-types.d.ts +2 -0
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/scheme-types.js.map +1 -1
- package/dist/core/types.d.ts +2 -26
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -16
- package/dist/core/types.js.map +1 -1
- package/dist/schemes/EffectPolicy.d.ts +6 -0
- package/dist/schemes/EffectPolicy.d.ts.map +1 -0
- package/dist/schemes/EffectPolicy.js +18 -0
- package/dist/schemes/EffectPolicy.js.map +1 -0
- package/dist/schemes/Exec.d.ts +2 -4
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +54 -36
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +9 -13
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Known.d.ts +2 -4
- package/dist/schemes/Known.d.ts.map +1 -1
- package/dist/schemes/Known.js +0 -6
- package/dist/schemes/Known.js.map +1 -1
- package/dist/schemes/Plurnk.d.ts +2 -4
- package/dist/schemes/Plurnk.d.ts.map +1 -1
- package/dist/schemes/Plurnk.js +0 -6
- package/dist/schemes/Plurnk.js.map +1 -1
- package/dist/schemes/Skill.d.ts +2 -4
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +0 -6
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Unknown.d.ts +2 -4
- package/dist/schemes/Unknown.d.ts.map +1 -1
- package/dist/schemes/Unknown.js +0 -6
- package/dist/schemes/Unknown.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +2 -3
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +6 -7
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +12 -14
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +1 -3
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +1 -59
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +13 -0
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/clientTurn.js +1 -1
- package/dist/server/clientTurn.js.map +1 -1
- package/dist/server/methods/loop_run.d.ts.map +1 -1
- package/dist/server/methods/loop_run.js.map +1 -1
- package/dist/server/methods/op_hide.js +1 -1
- package/dist/server/methods/op_hide.js.map +1 -1
- package/dist/server/methods/op_show.js +1 -1
- package/dist/server/methods/op_show.js.map +1 -1
- package/dist/server/noProposals.d.ts +6 -0
- package/dist/server/noProposals.d.ts.map +1 -0
- package/dist/server/noProposals.js +37 -0
- package/dist/server/noProposals.js.map +1 -0
- package/migrations/0000-00-00.01_schema.sql +0 -13
- package/package.json +27 -34
|
@@ -1,49 +1,15 @@
|
|
|
1
|
-
// Path-extension mimetype resolver for entry schemes
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Path-extension mimetype resolver for entry schemes — single source of truth
|
|
2
|
+
// is @plurnk/plurnk-schemes (keystone PR-1). Local OO facade over the
|
|
3
|
+
// daughter's function; call sites stay `PathMimetype.resolveEntryMimetype(...)`.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
// This makes structural ops (`<L>` item-index, matcher dialect dispatch)
|
|
11
|
-
// fire correctly without content-sniffing: a model writing JSON to
|
|
12
|
-
// `known://users.json` opts into JSON treatment via the `.json` suffix,
|
|
13
|
-
// while `known://notes` (no suffix) stays at Known's default of
|
|
14
|
-
// `text/markdown`.
|
|
15
|
-
//
|
|
16
|
-
// Consistent across:
|
|
17
|
-
// - known://users.json → application/json
|
|
18
|
-
// - known://notes.md → text/markdown
|
|
19
|
-
// - known://config.yaml → application/yaml
|
|
20
|
-
// - known://users → scheme default (text/markdown for Known)
|
|
21
|
-
import MimetypeBinary from "./mimetype-binary.js";
|
|
5
|
+
// A pathname's extension drives the effective mimetype (via the sibling
|
|
6
|
+
// Mimetypes detect service); the scheme manifest's channel default is the
|
|
7
|
+
// fallback when no extension is present. `known://users.json` →
|
|
8
|
+
// application/json; `known://notes` → scheme default.
|
|
9
|
+
import { resolveEntryMimetype as _resolveEntryMimetype } from "@plurnk/plurnk-schemes";
|
|
22
10
|
export default class PathMimetype {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
static #extractExtension(pathname) {
|
|
26
|
-
const lastSlash = pathname.lastIndexOf("/");
|
|
27
|
-
const lastSegment = lastSlash === -1 ? pathname : pathname.slice(lastSlash + 1);
|
|
28
|
-
const lastDot = lastSegment.lastIndexOf(".");
|
|
29
|
-
if (lastDot <= 0)
|
|
30
|
-
return ""; // leading-dot files (".env") aren't an extension
|
|
31
|
-
return lastSegment.slice(lastDot); // includes the leading "."
|
|
32
|
-
}
|
|
33
|
-
// Resolve a pathname → effective mimetype.
|
|
34
|
-
// 1. If the pathname has an extension, query Mimetypes.detect({ ext })
|
|
35
|
-
// and use what comes back (normalized via the text-primitive rule —
|
|
36
|
-
// text/plain → text/markdown).
|
|
37
|
-
// 2. Otherwise (no extension or no Mimetypes service), fall back to
|
|
38
|
-
// `schemeDefault` (typically the scheme manifest's channel default).
|
|
39
|
-
static async resolveEntryMimetype(pathname, schemeDefault, mimetypes) {
|
|
40
|
-
const ext = PathMimetype.#extractExtension(pathname);
|
|
41
|
-
if (ext.length > 0 && mimetypes !== undefined) {
|
|
42
|
-
const detected = await mimetypes.detect({ ext });
|
|
43
|
-
if (detected !== null)
|
|
44
|
-
return MimetypeBinary.normalizeAutoTextMimetype(detected);
|
|
45
|
-
}
|
|
46
|
-
return schemeDefault;
|
|
11
|
+
static resolveEntryMimetype(pathname, schemeDefault, mimetypes) {
|
|
12
|
+
return _resolveEntryMimetype(pathname, schemeDefault, mimetypes);
|
|
47
13
|
}
|
|
48
14
|
}
|
|
49
15
|
//# sourceMappingURL=path-mimetype.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-mimetype.js","sourceRoot":"","sources":["../../src/content/path-mimetype.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"path-mimetype.js","sourceRoot":"","sources":["../../src/content/path-mimetype.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,sEAAsE;AACtE,iFAAiF;AACjF,EAAE;AACF,wEAAwE;AACxE,0EAA0E;AAC1E,gEAAgE;AAChE,sDAAsD;AACtD,OAAO,EAAE,oBAAoB,IAAI,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAGvF,MAAM,CAAC,OAAO,OAAO,YAAY;IAC7B,MAAM,CAAC,oBAAoB,CAAC,QAAgB,EAAE,aAAqB,EAAE,SAAgC;QACjG,OAAO,qBAAqB,CAAC,QAAQ,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IACrE,CAAC;CACJ"}
|
|
@@ -2,6 +2,7 @@ import type { Db } from "./Db.ts";
|
|
|
2
2
|
export type ChannelState = "static" | "active" | "closed" | "errored";
|
|
3
3
|
export interface StreamEventPayload {
|
|
4
4
|
entryId: number;
|
|
5
|
+
target: string;
|
|
5
6
|
channel: string;
|
|
6
7
|
state: ChannelState;
|
|
7
8
|
contentLength: number;
|
|
@@ -11,6 +12,7 @@ export interface WakeRunPayload {
|
|
|
11
12
|
sessionId: number;
|
|
12
13
|
runId: number;
|
|
13
14
|
entryId: number;
|
|
15
|
+
target: string;
|
|
14
16
|
subscriptionId: number;
|
|
15
17
|
closeStatus: number;
|
|
16
18
|
scheme: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelWrite.d.ts","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEtE,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAQvF,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;AAQ9D,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ChannelWrite.d.ts","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEtE,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAQvF,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;AAQ9D,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAU/F,MAAM,CAAC,OAAO,OAAO,YAAY;;WAehB,eAAe,CACxB,EAAE,EAAE,EAAE,EACN,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,iBAAiB,CAAA;KAAE,GACrH,OAAO,CAAC,IAAI,CAAC;WASH,eAAe,CACxB,EAAE,EAAE,EAAE,EACN,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,YAAY,CAAC;QAAC,MAAM,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAC3H,OAAO,CAAC,IAAI,CAAC;WASH,gBAAgB,CACzB,EAAE,EAAE,EAAE,EACN,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GACvG,OAAO,CAAC,MAAM,CAAC;WAML,iBAAiB,CAC1B,EAAE,EAAE,EAAE,EACN,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GACvE,OAAO,CAAC,IAAI,CAAC;WAIH,sBAAsB,CAC/B,EAAE,EAAE,EAAE,EACN,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GACvD,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAIpE"}
|
|
@@ -12,6 +12,12 @@ export default class ChannelWrite {
|
|
|
12
12
|
static #openSubStmt(db) { return db.open_subscription; }
|
|
13
13
|
static #closeSubStmt(db) { return db.close_subscription; }
|
|
14
14
|
static #findActiveStmt(db) { return db.find_active_subscription; }
|
|
15
|
+
// The entry's target URI for stream notifications (#179). A NULL scheme is
|
|
16
|
+
// a filesystem entry (the file scheme stores scheme=NULL), so it decodes to
|
|
17
|
+
// file://.
|
|
18
|
+
static #targetUri(scheme, pathname) {
|
|
19
|
+
return `${scheme === null ? "file" : scheme}://${pathname}`;
|
|
20
|
+
}
|
|
15
21
|
static async appendToChannel(db, { entryId, channel, chunk, notify }) {
|
|
16
22
|
const result = await ChannelWrite.#appendStmt(db).run({ chunk, entry_id: entryId, channel });
|
|
17
23
|
if (result.changes === 0)
|
|
@@ -21,7 +27,7 @@ export default class ChannelWrite {
|
|
|
21
27
|
const meta = await ChannelWrite.#channelMeta(db).get({ entry_id: entryId, channel });
|
|
22
28
|
if (meta === undefined)
|
|
23
29
|
return;
|
|
24
|
-
notify(meta.session_id, { entryId, channel, state: meta.state, contentLength: meta.contentLength });
|
|
30
|
+
notify(meta.session_id, { entryId, target: ChannelWrite.#targetUri(meta.scheme, meta.pathname), channel, state: meta.state, contentLength: meta.contentLength });
|
|
25
31
|
}
|
|
26
32
|
static async setChannelState(db, { entryId, channel, state, notify }) {
|
|
27
33
|
const result = await ChannelWrite.#stateStmt(db).run({ state, entry_id: entryId, channel });
|
|
@@ -32,7 +38,7 @@ export default class ChannelWrite {
|
|
|
32
38
|
const meta = await ChannelWrite.#channelMeta(db).get({ entry_id: entryId, channel });
|
|
33
39
|
if (meta === undefined)
|
|
34
40
|
return;
|
|
35
|
-
notify(meta.session_id, { entryId, channel, state: meta.state, contentLength: meta.contentLength });
|
|
41
|
+
notify(meta.session_id, { entryId, target: ChannelWrite.#targetUri(meta.scheme, meta.pathname), channel, state: meta.state, contentLength: meta.contentLength });
|
|
36
42
|
}
|
|
37
43
|
static async openSubscription(db, { runId, entryId, scheme, handle }) {
|
|
38
44
|
const row = await ChannelWrite.#openSubStmt(db).get({ run_id: runId, entry_id: entryId, scheme, handle });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelWrite.js","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,mEAAmE;AACnE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,2EAA2E;AAC3E,gCAAgC;
|
|
1
|
+
{"version":3,"file":"ChannelWrite.js","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"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"}
|
package/dist/core/Engine.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type SchemeRegistry from "./SchemeRegistry.ts";
|
|
|
3
3
|
import { Mimetypes } from "@plurnk/plurnk-mimetypes";
|
|
4
4
|
import type { Db } from "./Db.ts";
|
|
5
5
|
import type { LoopFlags } from "./scheme-types.ts";
|
|
6
|
+
import type ExecutorRegistry from "./ExecutorRegistry.ts";
|
|
6
7
|
import type { StreamEventNotify, TelemetryEventNotify, WakeRunNotify } from "./ChannelWrite.ts";
|
|
7
8
|
type Origin = "model" | "client" | "system" | "plugin";
|
|
8
9
|
type ChatMessage = {
|
|
@@ -66,6 +67,7 @@ export default class Engine {
|
|
|
66
67
|
telemetryEventNotify?: TelemetryEventNotify;
|
|
67
68
|
tokenize?: (text: string) => number;
|
|
68
69
|
});
|
|
70
|
+
setExecutors(executors: ExecutorRegistry): void;
|
|
69
71
|
runLoop({ provider, messages, persona, requirements, sessionId, runId, loopId, maxTurns, maxStrikes, minCycles, maxCyclePeriod, origin, signal, onDispatch, }: {
|
|
70
72
|
provider: Provider;
|
|
71
73
|
messages: ChatMessage[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAA4D,MAAM,wBAAwB,CAAC;AAMxH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAiB,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAK9C,OAAO,KAAK,EAAmD,SAAS,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"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"}
|
package/dist/core/Engine.js
CHANGED
|
@@ -6,12 +6,12 @@ import EntryManifest from "../schemes/_entry-manifest.js";
|
|
|
6
6
|
import GitMembership from "./git-membership.js";
|
|
7
7
|
import { DEFAULT_LOOP_FLAGS } from "./scheme-types.js";
|
|
8
8
|
import { LineMarkerOps, MimetypeBinary } from "../content/index.js";
|
|
9
|
-
//
|
|
10
|
-
// digest projection are structurally one function
|
|
11
|
-
//
|
|
9
|
+
// Shared module imported by both Engine and bin/digest.ts, so wire
|
|
10
|
+
// projection and digest projection are structurally one function — no
|
|
11
|
+
// drift between wire and digest possible.
|
|
12
12
|
import PacketWire from "./packet-wire.js";
|
|
13
13
|
// SPEC §3.6: writer must be in target scheme's manifest.writableBy.
|
|
14
|
-
// SHOW/HIDE/READ/FIND are not gated — they
|
|
14
|
+
// SHOW/HIDE/READ/FIND are not gated — they curate the log or read, never mutating an entry.
|
|
15
15
|
const MUTATING_OPS = new Set(["EDIT", "SEND", "COPY", "MOVE", "EXEC"]);
|
|
16
16
|
const DEFAULT_PREVIEW_BUDGET = 256;
|
|
17
17
|
const DEFAULT_MAX_STRIKES = 3;
|
|
@@ -202,6 +202,9 @@ class Engine {
|
|
|
202
202
|
// construction before a provider is wired (same boot affordance as
|
|
203
203
|
// Mimetypes, §4.5). Real counts come from provider.countTokens.
|
|
204
204
|
#tokenize;
|
|
205
|
+
// Boot-discovered runtime executors. Daemon builds + sets via
|
|
206
|
+
// setExecutors at start(); undefined until then (and in bare tests).
|
|
207
|
+
#executors;
|
|
205
208
|
// Per-loop transient buffer of actionless failures pending surface in the
|
|
206
209
|
// NEXT packet's user.telemetry.errors[]. Drained by #buildTelemetryErrors.
|
|
207
210
|
// Map<loopId, TelemetryError[]>. SPEC §15.1.
|
|
@@ -256,6 +259,11 @@ class Engine {
|
|
|
256
259
|
// wired by the Daemon. Real counts come from provider.countTokens.
|
|
257
260
|
this.#tokenize = tokenize ?? ((text) => Math.ceil(text.length / 4));
|
|
258
261
|
}
|
|
262
|
+
// Late injection: the executor registry is async-built at daemon start()
|
|
263
|
+
// (discover + probe), after Engine construction.
|
|
264
|
+
setExecutors(executors) {
|
|
265
|
+
this.#executors = executors;
|
|
266
|
+
}
|
|
259
267
|
#pushTelemetry(sessionId, loopId, event) {
|
|
260
268
|
const existing = this.#telemetryBuffer.get(loopId);
|
|
261
269
|
if (existing === undefined)
|
|
@@ -479,9 +487,9 @@ class Engine {
|
|
|
479
487
|
// prompt-composition (EMI is eager + relevance-bounded). When the
|
|
480
488
|
// session's project_root is a git working tree, tracked files are
|
|
481
489
|
// members without a client `add`; active members are materialized
|
|
482
|
-
// (disk → body channel
|
|
483
|
-
//
|
|
484
|
-
//
|
|
490
|
+
// (disk → body channel) so they appear in the manifest below. No-ops
|
|
491
|
+
// on headless / non-git sessions. Runs BEFORE the manifest write so
|
|
492
|
+
// this turn's packet reflects them.
|
|
485
493
|
await GitMembership.indexGitMembership(systemCtx);
|
|
486
494
|
await EntryCrud.writeEntry("manifest.json", {
|
|
487
495
|
channels: { body: { content: await EntryManifest.buildManifestBody(systemCtx, this.#previewBudget), mimetype: "application/json" } },
|
|
@@ -651,6 +659,16 @@ class Engine {
|
|
|
651
659
|
}
|
|
652
660
|
}
|
|
653
661
|
}
|
|
662
|
+
// The grammar also reports an `unparsedTail` when input ends
|
|
663
|
+
// mid-statement (a body opened but never closed): its `reason`
|
|
664
|
+
// names the op AND the fix ("…never closed — add `:READ`"), where
|
|
665
|
+
// the item-level error only says "expected close tag" for a tag the
|
|
666
|
+
// model thinks it already wrote. Surface it — phenomenal messages
|
|
667
|
+
// the model can self-correct from are the whole point of the DSL.
|
|
668
|
+
const tail = parsed.unparsedTail;
|
|
669
|
+
if (tail !== undefined) {
|
|
670
|
+
parseErrors.push({ message: tail.reason, line: tail.from.line, column: tail.from.column, source: "grammar" });
|
|
671
|
+
}
|
|
654
672
|
}
|
|
655
673
|
const wireReasoning = assistant.reasoning ?? "";
|
|
656
674
|
const scrapedReasoning = textFragments.join("\n");
|
|
@@ -683,7 +701,6 @@ class Engine {
|
|
|
683
701
|
// query; null result means no DB override exists, use the default.
|
|
684
702
|
const row = await this.#db.engine_resolve_persona.get({ loop_id: loopId });
|
|
685
703
|
const persona = (row?.persona !== undefined && row?.persona !== null) ? row.persona : defaultPersona;
|
|
686
|
-
const index = await this.#buildIndex(runId, loopId);
|
|
687
704
|
const log = await this.#buildLog(runId);
|
|
688
705
|
const telemetryErrors = presetTelemetry ?? await this.#buildTelemetryErrors(loopId, currentTurnSeq);
|
|
689
706
|
// Per-section render-cost subtotals via provider's tokenizer.
|
|
@@ -701,7 +718,7 @@ class Engine {
|
|
|
701
718
|
// headline omitted, section lines still shown).
|
|
702
719
|
const ceiling = _a.computeCeiling(provider.contextSize, this.#budgetCeiling);
|
|
703
720
|
const scratch = {
|
|
704
|
-
system: { system_definition, persona,
|
|
721
|
+
system: { system_definition, persona, log },
|
|
705
722
|
user: { prompt, telemetry: { budget: "", errors: telemetryErrors }, system_requirements: requirements },
|
|
706
723
|
};
|
|
707
724
|
const sections = PacketWire.measureBudgetSections(scratch, countTokens);
|
|
@@ -715,7 +732,7 @@ class Engine {
|
|
|
715
732
|
.replace(TOKEN_USAGE_PLACEHOLDER, String(total))
|
|
716
733
|
.replace(TOKEN_PERCENT_PLACEHOLDER, String(percent))
|
|
717
734
|
.replace(TOKENS_FREE_PLACEHOLDER, String(tokensFree));
|
|
718
|
-
const system = { tokens: 0, system_definition, persona,
|
|
735
|
+
const system = { tokens: 0, system_definition, persona, log };
|
|
719
736
|
const user = { tokens: 0, prompt, telemetry: { budget, errors: telemetryErrors }, system_requirements: requirements };
|
|
720
737
|
system.tokens = countTokens(PacketWire.renderSystemContent(system));
|
|
721
738
|
user.tokens = countTokens(PacketWire.renderUserContent(user));
|
|
@@ -729,8 +746,6 @@ class Engine {
|
|
|
729
746
|
const lines = [];
|
|
730
747
|
if (ceiling !== null)
|
|
731
748
|
lines.push(`ceiling ${ceiling} · usage ${TOKEN_USAGE_PLACEHOLDER} (${TOKEN_PERCENT_PLACEHOLDER}%) · free ${TOKENS_FREE_PLACEHOLDER}`);
|
|
732
|
-
if (sections.index.channels > 0)
|
|
733
|
-
lines.push(`Index previews: ${sections.index.channels} channels, ${sections.index.tokens} tokens`);
|
|
734
749
|
if (sections.log.entries > 0) {
|
|
735
750
|
lines.push(`Log entries: ${sections.log.entries} entries, ${sections.log.tokens} tokens`);
|
|
736
751
|
if (sections.log.byScheme.length > 0) {
|
|
@@ -766,15 +781,9 @@ class Engine {
|
|
|
766
781
|
this.#emitBudgetOverflow(sessionId, loopId, hidden);
|
|
767
782
|
return { packet: current, fit: true, struck: turnNumber > 1 };
|
|
768
783
|
}
|
|
769
|
-
//
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
note(c.scheme);
|
|
773
|
-
if (catalog.length > 0) {
|
|
774
|
-
const pairs = JSON.stringify(catalog.map((c) => ({ entry_id: c.entry_id, channel: c.channel })));
|
|
775
|
-
await this.#db.engine_grinder_hide_catalog.run({ run_id: runId, pairs });
|
|
776
|
-
}
|
|
777
|
-
current = catalog.length > 0 ? await rebuild(errors) : current;
|
|
784
|
+
// Prior-turn rollback is the only budget lever now: entries don't render
|
|
785
|
+
// (no index), so there is no catalog to collapse. If pass 1 didn't fit,
|
|
786
|
+
// the packet is over and the caller hard-413s.
|
|
778
787
|
this.#emitBudgetOverflow(sessionId, loopId, hidden);
|
|
779
788
|
return { packet: current, fit: measure(current) <= ceiling, struck: turnNumber > 1 };
|
|
780
789
|
}
|
|
@@ -790,8 +799,8 @@ class Engine {
|
|
|
790
799
|
hidden: [...hidden.entries()].map(([scheme, count]) => ({ scheme, count })),
|
|
791
800
|
});
|
|
792
801
|
}
|
|
793
|
-
// Wire projection lives in ./packet-wire.
|
|
794
|
-
// bin/digest.
|
|
802
|
+
// Wire projection lives in ./packet-wire.ts so Engine and
|
|
803
|
+
// bin/digest.ts import the exact same function — structurally one
|
|
795
804
|
// implementation, no drift between wire and digest possible.
|
|
796
805
|
// Format: markdown (user pick over rummy's XML alternative, 2026-05-22).
|
|
797
806
|
#packetToWireMessages(packet) {
|
|
@@ -874,66 +883,6 @@ class Engine {
|
|
|
874
883
|
mimetype_tx: r.mimetype_tx,
|
|
875
884
|
}));
|
|
876
885
|
}
|
|
877
|
-
async #buildIndex(runId, currentLoopId) {
|
|
878
|
-
const rows = await this.#db.engine_render_index.all({ run_id: runId });
|
|
879
|
-
const tagsStmt = this.#db.engine_entry_tags;
|
|
880
|
-
// Foist the CURRENT loop's prompt entries out of the index render —
|
|
881
|
-
// their bodies are materialized in packet.user.prompt instead. With
|
|
882
|
-
// multi-turn injection, a loop can have multiple prompt entries at
|
|
883
|
-
// plurnk://prompt/<loop_id>/<N>; all of them get foisted, leaving
|
|
884
|
-
// only previous loops' prompts (still addressable for HIDE).
|
|
885
|
-
const foistedPrefix = `prompt/${currentLoopId}/`;
|
|
886
|
-
// Backward compat: legacy single-slot path. Tests / older runs may
|
|
887
|
-
// still have a `prompt/<loop_id>` entry (no trailing /N); foist it too.
|
|
888
|
-
const foistedExact = `prompt/${currentLoopId}`;
|
|
889
|
-
const entries = new Map();
|
|
890
|
-
for (const row of rows) {
|
|
891
|
-
if (row.scheme === "plurnk"
|
|
892
|
-
&& (row.pathname === foistedExact || row.pathname.startsWith(foistedPrefix)))
|
|
893
|
-
continue;
|
|
894
|
-
let entry = entries.get(row.entry_id);
|
|
895
|
-
if (entry === undefined) {
|
|
896
|
-
const tagRows = await tagsStmt.all({ entry_id: row.entry_id });
|
|
897
|
-
// defaultChannel pulled from the row's scheme manifest. The
|
|
898
|
-
// wire renderer uses it to omit `#channel` on fences whose
|
|
899
|
-
// channel is the scheme's default — that absence is the
|
|
900
|
-
// addressing of the default channel.
|
|
901
|
-
const handler = this.#schemes.get(row.scheme ?? "");
|
|
902
|
-
const manifest = handler?.constructor?.manifest;
|
|
903
|
-
entry = {
|
|
904
|
-
id: row.entry_id,
|
|
905
|
-
version: row.version,
|
|
906
|
-
scope: row.scope,
|
|
907
|
-
session_id: row.session_id,
|
|
908
|
-
scheme: row.scheme,
|
|
909
|
-
username: row.username,
|
|
910
|
-
password: row.password,
|
|
911
|
-
hostname: row.hostname,
|
|
912
|
-
port: row.port,
|
|
913
|
-
pathname: row.pathname,
|
|
914
|
-
params: row.params === null ? null : JSON.parse(row.params),
|
|
915
|
-
channels: {},
|
|
916
|
-
defaultChannel: manifest?.defaultChannel ?? "",
|
|
917
|
-
attributes: JSON.parse(row.attributes),
|
|
918
|
-
tags: tagRows.map((r) => r.tag),
|
|
919
|
-
};
|
|
920
|
-
entries.set(row.entry_id, entry);
|
|
921
|
-
}
|
|
922
|
-
// Mimetypes.process owns the full preview pipeline: detect (or
|
|
923
|
-
// honor the hint), resolve handler, validate, extract → symbols,
|
|
924
|
-
// budget-truncate via the framework's fit/fitContent. Passing
|
|
925
|
-
// `hint: row.mimetype` short-circuits detection — service already
|
|
926
|
-
// knows what each channel is.
|
|
927
|
-
const result = await this.#mimetypes.process({ content: row.content, hint: row.mimetype }, { budget: this.#previewBudget });
|
|
928
|
-
entry.channels[row.channel] = {
|
|
929
|
-
content: result.preview,
|
|
930
|
-
mimetype: row.mimetype,
|
|
931
|
-
tokens: row.tokens,
|
|
932
|
-
lines: result.totalLines,
|
|
933
|
-
};
|
|
934
|
-
}
|
|
935
|
-
return [...entries.values()];
|
|
936
|
-
}
|
|
937
886
|
async dispatch(context) {
|
|
938
887
|
const { statement, sessionId, runId, loopId, turnId, sequence, origin, onDispatch } = context;
|
|
939
888
|
const schemeCtx = {
|
|
@@ -946,6 +895,7 @@ class Engine {
|
|
|
946
895
|
mimetypes: this.#mimetypes,
|
|
947
896
|
tokenize: this.#tokenize,
|
|
948
897
|
pushTelemetry: (event) => this.#pushTelemetry(sessionId, loopId, event),
|
|
898
|
+
executors: this.#executors,
|
|
949
899
|
};
|
|
950
900
|
let result;
|
|
951
901
|
let denial = this.#checkWritable(statement, origin);
|
|
@@ -998,6 +948,13 @@ class Engine {
|
|
|
998
948
|
// 202 in the result the caller sees, so runTurn never branches on
|
|
999
949
|
// a pending state.
|
|
1000
950
|
if (result.status === 202) {
|
|
951
|
+
// Effect-gated auto-run (read/pure runtimes, plurnk-service#182):
|
|
952
|
+
// no human gate, no loop/proposal notification. Accept + apply
|
|
953
|
+
// in-process; the model sees the outcome directly, never a review.
|
|
954
|
+
if (result.attrs?.inline === true) {
|
|
955
|
+
const effective = await this.#runApplyResolution(statement, result, { decision: "accept" }, { sessionId, runId, loopId, turnId });
|
|
956
|
+
return this.#applyResolution(logEntryId, effective);
|
|
957
|
+
}
|
|
1001
958
|
// Register the resolution waiter SYNCHRONOUSLY before any await
|
|
1002
959
|
// yields. A same-tick resolveProposal() (e.g. from a test that
|
|
1003
960
|
// awaits the onDispatch callback and immediately resolves) must
|
|
@@ -1060,6 +1017,7 @@ class Engine {
|
|
|
1060
1017
|
wakeRunNotify: this.#wakeRunNotify,
|
|
1061
1018
|
tokenize: this.#tokenize,
|
|
1062
1019
|
pushTelemetry: (event) => this.#pushTelemetry(sessionId, loopId, event),
|
|
1020
|
+
executors: this.#executors,
|
|
1063
1021
|
};
|
|
1064
1022
|
const applyResult = await handler.applyResolution({
|
|
1065
1023
|
attrs: (originalResult.attrs ?? {}),
|
|
@@ -1073,14 +1031,14 @@ class Engine {
|
|
|
1073
1031
|
};
|
|
1074
1032
|
}
|
|
1075
1033
|
// Propagate applyResolution.outcome onto the accepted resolution
|
|
1076
|
-
//
|
|
1077
|
-
// (
|
|
1078
|
-
//
|
|
1079
|
-
//
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
return
|
|
1034
|
+
// (operational metadata, e.g. exec's "exit_N") AND its body — an
|
|
1035
|
+
// inline (read/pure) run returns its output as the body, which has
|
|
1036
|
+
// to reach the model-facing result this turn, not just stream to
|
|
1037
|
+
// the entry. Host accepts carry no body (fire-and-forget).
|
|
1038
|
+
const withOutcome = applyResult.outcome !== undefined && resolution.outcome === undefined
|
|
1039
|
+
? { ...resolution, outcome: applyResult.outcome }
|
|
1040
|
+
: resolution;
|
|
1041
|
+
return applyResult.body === undefined ? withOutcome : { ...withOutcome, body: applyResult.body };
|
|
1084
1042
|
}
|
|
1085
1043
|
catch (err) {
|
|
1086
1044
|
return {
|
|
@@ -1216,13 +1174,15 @@ class Engine {
|
|
|
1216
1174
|
: decision === "reject" ? "rejected"
|
|
1217
1175
|
: "loop_aborted";
|
|
1218
1176
|
const outcome = resolution.outcome ?? defaultOutcome;
|
|
1219
|
-
// rx is the model-facing operation result.
|
|
1220
|
-
//
|
|
1221
|
-
//
|
|
1222
|
-
//
|
|
1223
|
-
//
|
|
1177
|
+
// rx is the model-facing operation result. Status always; outcome is
|
|
1178
|
+
// operational (stays on log_entries for forensics, never model-facing).
|
|
1179
|
+
// Body is normally dropped — the propose preview was an input echo —
|
|
1180
|
+
// EXCEPT an inline auto-run (read/pure) carries its run output AS the
|
|
1181
|
+
// body, which is exactly the "what happened" the model needs this turn.
|
|
1224
1182
|
// Per AGENTS.md "Operational hygiene on what the model sees."
|
|
1225
|
-
const rx =
|
|
1183
|
+
const rx = (decision === "accept" && resolution.body !== undefined)
|
|
1184
|
+
? JSON.stringify({ status, body: resolution.body })
|
|
1185
|
+
: JSON.stringify({ status });
|
|
1226
1186
|
await this.#db.engine_resolve_log_entry.run({
|
|
1227
1187
|
id: logEntryId, state, outcome, status_rx: status, rx,
|
|
1228
1188
|
});
|
|
@@ -1277,7 +1237,7 @@ class Engine {
|
|
|
1277
1237
|
return { status: 403, error: `writer '${origin}' is not in writableBy for scheme '${schemeName}'` };
|
|
1278
1238
|
}
|
|
1279
1239
|
// Per-loop flag gating. Schemes self-declare their flag affinity in
|
|
1280
|
-
// their manifest (
|
|
1240
|
+
// their manifest (excludedInAsk / requiresWeb /
|
|
1281
1241
|
// requiresInteraction); SchemeRegistry.resolveForLoop returns the
|
|
1282
1242
|
// active set under the loop's persisted flags. Anything outside the
|
|
1283
1243
|
// set returns 403 — action-entry-as-outcome carries the rejection.
|
|
@@ -1287,7 +1247,7 @@ class Engine {
|
|
|
1287
1247
|
return null;
|
|
1288
1248
|
const flags = await this.#loadLoopFlags(loopId);
|
|
1289
1249
|
// Fast path: default flags gate nothing. (yolo never gates.)
|
|
1290
|
-
if (!flags.
|
|
1250
|
+
if (!flags.noWeb && !flags.noInteraction && flags.mode === "act")
|
|
1291
1251
|
return null;
|
|
1292
1252
|
const active = this.#schemes.resolveForLoop(flags);
|
|
1293
1253
|
const check = (target) => {
|
|
@@ -1409,7 +1369,7 @@ class Engine {
|
|
|
1409
1369
|
const dstNames = Object.keys(dstChannels).sort();
|
|
1410
1370
|
const sameContent = writeNames.length === dstNames.length
|
|
1411
1371
|
&& writeNames.every((n, i) => n === dstNames[i] && (channels[n]?.content ?? "") === (dstChannels[n]?.content ?? ""));
|
|
1412
|
-
const sameTags = [...tags].sort().join("
|
|
1372
|
+
const sameTags = [...tags].sort().join("") === [...dstExisting.entry.tags].sort().join("");
|
|
1413
1373
|
if (sameContent && sameTags)
|
|
1414
1374
|
return { status: 304 };
|
|
1415
1375
|
return { status: 409, error: `COPY/MOVE destination exists: ${dstSchemeName}://${dstPathname}` };
|
|
@@ -1465,19 +1425,23 @@ class Engine {
|
|
|
1465
1425
|
let attrsObj = (result.attrs !== undefined && result.attrs !== null)
|
|
1466
1426
|
? { ...result.attrs }
|
|
1467
1427
|
: {};
|
|
1468
|
-
// EXEC pathname is
|
|
1469
|
-
// exec://<loop_seq>/<turn_seq>/<sequence
|
|
1470
|
-
//
|
|
1471
|
-
//
|
|
1472
|
-
//
|
|
1473
|
-
//
|
|
1428
|
+
// EXEC pathname is executor-domain + coordinate: the stream entry
|
|
1429
|
+
// lives at exec://<runtime>/<loop_seq>/<turn_seq>/<sequence> (e.g.
|
|
1430
|
+
// exec://sh/1/1/2). The runtime leads — domain-aware, the executor
|
|
1431
|
+
// as authority — and the coordinate that follows is already unique
|
|
1432
|
+
// per statement, so no slug is injected. The log row's target points
|
|
1433
|
+
// at this same address; its log:// coordinate shares the trailing
|
|
1434
|
+
// <loop>/<turn>/<seq>, so the model correlates op to stream output.
|
|
1435
|
+
// Runtime comes from statement.signal (EXEC's runtime slot) so it's
|
|
1436
|
+
// resolvable for failed execs too; empty/absent = the default shell.
|
|
1474
1437
|
if (statement.op === "EXEC") {
|
|
1475
1438
|
const seqs = await this.#db.engine_loop_turn_seqs.get({
|
|
1476
1439
|
loop_id: loopId, turn_id: turnId,
|
|
1477
1440
|
});
|
|
1478
1441
|
if (seqs === undefined)
|
|
1479
1442
|
throw new Error(`Engine.#writeLog: loop_turn_seqs returned no row for loop=${loopId} turn=${turnId}`);
|
|
1480
|
-
const
|
|
1443
|
+
const runtime = (typeof statement.signal === "string" && statement.signal.length > 0) ? statement.signal : "sh";
|
|
1444
|
+
const coordPathname = `${runtime}/${seqs.loop_seq}/${seqs.turn_seq}/${sequence}`;
|
|
1481
1445
|
target.scheme = "exec";
|
|
1482
1446
|
target.pathname = coordPathname;
|
|
1483
1447
|
attrsObj.pathname = coordPathname;
|
|
@@ -1485,8 +1449,7 @@ class Engine {
|
|
|
1485
1449
|
// hands originalResult.attrs to handler.applyResolution after
|
|
1486
1450
|
// proposal accept (see #acceptResolution). Both views — the
|
|
1487
1451
|
// stored row AND the in-memory proposal — need the same
|
|
1488
|
-
//
|
|
1489
|
-
// entry at exec://<coord>/EXEC.
|
|
1452
|
+
// pathname so applyResolution writes the entry at the same URI.
|
|
1490
1453
|
if (result.attrs !== undefined && result.attrs !== null) {
|
|
1491
1454
|
result.attrs.pathname = coordPathname;
|
|
1492
1455
|
}
|