@avantf/dsh-identity 0.0.0-stage → 0.1.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -2
  3. package/cordis.patch.yml +13 -0
  4. package/lib/assets/presets/analyst/en/IDENTITY.md +1 -0
  5. package/lib/assets/presets/analyst/en/RULES.md +8 -0
  6. package/lib/assets/presets/analyst/en/SOUL.md +5 -0
  7. package/lib/assets/presets/analyst/zh/IDENTITY.md +1 -0
  8. package/lib/assets/presets/analyst/zh/RULES.md +8 -0
  9. package/lib/assets/presets/analyst/zh/SOUL.md +5 -0
  10. package/lib/assets/presets/assistant/en/IDENTITY.md +1 -0
  11. package/lib/assets/presets/assistant/en/RULES.md +8 -0
  12. package/lib/assets/presets/assistant/en/SOUL.md +5 -0
  13. package/lib/assets/presets/assistant/zh/IDENTITY.md +1 -0
  14. package/lib/assets/presets/assistant/zh/RULES.md +8 -0
  15. package/lib/assets/presets/assistant/zh/SOUL.md +5 -0
  16. package/lib/assets/presets/coder/en/IDENTITY.md +1 -0
  17. package/lib/assets/presets/coder/en/RULES.md +8 -0
  18. package/lib/assets/presets/coder/en/SOUL.md +5 -0
  19. package/lib/assets/presets/coder/zh/IDENTITY.md +1 -0
  20. package/lib/assets/presets/coder/zh/RULES.md +8 -0
  21. package/lib/assets/presets/coder/zh/SOUL.md +5 -0
  22. package/lib/client.js +61 -0
  23. package/lib/dsh-build.json +6 -0
  24. package/lib/envinit-bootstrap.js +341 -0
  25. package/lib/index.js +1517 -0
  26. package/lib/interface-version.json +4 -0
  27. package/lib/types/atomic-write.d.ts +3 -0
  28. package/lib/types/atomic-write.d.ts.map +1 -0
  29. package/lib/types/base.d.ts +45 -0
  30. package/lib/types/base.d.ts.map +1 -0
  31. package/lib/types/client/IdentitySection.d.ts +29 -0
  32. package/lib/types/client/IdentitySection.d.ts.map +1 -0
  33. package/lib/types/client/api.d.ts +204 -0
  34. package/lib/types/client/api.d.ts.map +1 -0
  35. package/lib/types/client/index.d.ts +57 -0
  36. package/lib/types/client/index.d.ts.map +1 -0
  37. package/lib/types/documents.d.ts +65 -0
  38. package/lib/types/documents.d.ts.map +1 -0
  39. package/lib/types/envinit.d.ts +22 -0
  40. package/lib/types/envinit.d.ts.map +1 -0
  41. package/lib/types/gateway.d.ts +124 -0
  42. package/lib/types/gateway.d.ts.map +1 -0
  43. package/lib/types/index.d.ts +97 -0
  44. package/lib/types/index.d.ts.map +1 -0
  45. package/lib/types/interface_gate.d.ts +121 -0
  46. package/lib/types/interface_gate.d.ts.map +1 -0
  47. package/lib/types/log.d.ts +18 -0
  48. package/lib/types/log.d.ts.map +1 -0
  49. package/lib/types/paths.d.ts +45 -0
  50. package/lib/types/paths.d.ts.map +1 -0
  51. package/lib/types/presets.d.ts +201 -0
  52. package/lib/types/presets.d.ts.map +1 -0
  53. package/lib/types/session-freeze.d.ts +33 -0
  54. package/lib/types/session-freeze.d.ts.map +1 -0
  55. package/lib/types/state.d.ts +190 -0
  56. package/lib/types/state.d.ts.map +1 -0
  57. package/lib/types/versions.d.ts +19 -0
  58. package/lib/types/versions.d.ts.map +1 -0
  59. package/lib/types/volatile.d.ts +28 -0
  60. package/lib/types/volatile.d.ts.map +1 -0
  61. package/lib/types/wire.d.ts +86 -0
  62. package/lib/types/wire.d.ts.map +1 -0
  63. package/package.json +104 -3
@@ -0,0 +1,97 @@
1
+ /**
2
+ * `@avantf/dsh-identity` — the host half.
3
+ *
4
+ * The mechanism is the spec's §3.2 and nothing else:
5
+ *
6
+ * ① an ordinary section named `avantf:identity` is registered in the HARNESS IDENTITY's own slot
7
+ * (`getSectionOrder('HARNESS_IDENTITY')` → -1000), whose text is the three files rendered;
8
+ * ② a `system-prompt/assemble` waterfall listener removes TWO NAMED sections
9
+ * (`harness:identity`, `deployment:persona-prefix`) and changes nothing else.
10
+ *
11
+ * `complete: true` is deliberately NOT used: that flag replaces the WHOLE section list, which would
12
+ * take the ~20 capability-guidance sections the rest of the deployment registers with it. The
13
+ * waterfall's return value is authoritative exactly because no complete section is registered.
14
+ *
15
+ * Scope: `replaceScope: 'session'` (the default) stops at the main agent, so a delegated child keeps
16
+ * its native identity — including the persona `dsh-subagent` gives it. That is `isMainAgent` below,
17
+ * and it is asserted byte-for-byte in `test/mechanism.spec.ts`.
18
+ *
19
+ * @module @avantf/dsh-identity
20
+ */
21
+ import type { Context, Volatile } from '@deepseek-ai/cordis';
22
+ import { type AssembleContext } from '@deepseek-ai/dsh-system-prompt';
23
+ import z from '@deepseek-ai/schemastery';
24
+ /** Cordis plugin name; matches the row id in the profile and the settings namespace. */
25
+ export declare const name = "avantf-identity";
26
+ /**
27
+ * The prompt service is the ONLY required service: without it there is no mechanism to install, and
28
+ * `typert` / `settings` / `profileContext` are all read optionally so a headless composition (and the
29
+ * mechanism spec's bare Context) mounts in full.
30
+ */
31
+ export declare const inject: string[];
32
+ /** The section this plugin owns, and the two the default `drop` list names. */
33
+ export declare const OWN_SECTION = "avantf:identity";
34
+ /** The harness identity's section name. A literal upstream (`PromptSectionOrderName` is a NUMBER), pinned by a spec. */
35
+ export declare const HARNESS_IDENTITY_SECTION = "harness:identity";
36
+ /** The default drop list: the two identity-bearing sections, never the persona SUFFIX (it is information). */
37
+ export declare const DEFAULT_DROP: readonly string[];
38
+ /** The default byte budget for the rendered identity. `0` disables the budget. */
39
+ export declare const DEFAULT_MAX_BYTES = 65536;
40
+ export interface Config {
41
+ /** The master switch: `true` replaces the identity, `false` (the default) leaves the prompt native. */
42
+ enabled?: Volatile<boolean | undefined>;
43
+ /** Whether `{{variable}}` references in the files are interpolated (default false: the file is literal text). */
44
+ interpolate?: Volatile<boolean | undefined>;
45
+ /** `session` (default) replaces the main agent's identity only; `all` also replaces delegated children's. */
46
+ replaceScope?: Volatile<'session' | 'all' | undefined>;
47
+ /** The section NAMES the waterfall removes when the switch is on. */
48
+ drop?: Volatile<readonly string[] | undefined>;
49
+ /** Byte budget for the rendered identity; over it, the text is truncated with a visible notice. */
50
+ maxBytes?: Volatile<number | undefined>;
51
+ /** Overrides the profile name when no `profileContext` is present (default: the profile context's name, else `default`). */
52
+ profile?: Volatile<string | undefined>;
53
+ /** Layer ② of the family data-root rule, BELOW `$AVANTF_HOME`. */
54
+ dataHome?: Volatile<string | undefined>;
55
+ /** The preset last applied through the settings page (recorded; empty means "custom"). */
56
+ activePreset?: Volatile<string | undefined>;
57
+ /** The locale that preset was applied in, so a language change can offer a re-apply. */
58
+ activeLocale?: Volatile<string | undefined>;
59
+ }
60
+ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
61
+ enabled: z<boolean, boolean, "volatile-defined">;
62
+ interpolate: z<boolean, boolean, "volatile-defined">;
63
+ replaceScope: z<"session" | "all", "session" | "all", "volatile-defined">;
64
+ drop: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
65
+ maxBytes: z<number, number, "volatile-defined">;
66
+ profile: z<string, string, "volatile-defined">;
67
+ dataHome: z<string, string, "volatile-defined">;
68
+ activePreset: z<string, string, "volatile-defined">;
69
+ activeLocale: z<string, string, "volatile-defined">;
70
+ }>>, Schemastery.ObjectT<NoInfer<{
71
+ enabled: z<boolean, boolean, "volatile-defined">;
72
+ interpolate: z<boolean, boolean, "volatile-defined">;
73
+ replaceScope: z<"session" | "all", "session" | "all", "volatile-defined">;
74
+ drop: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
75
+ maxBytes: z<number, number, "volatile-defined">;
76
+ profile: z<string, string, "volatile-defined">;
77
+ dataHome: z<string, string, "volatile-defined">;
78
+ activePreset: z<string, string, "volatile-defined">;
79
+ activeLocale: z<string, string, "volatile-defined">;
80
+ }>>, "plain">;
81
+ /**
82
+ * Whether this assembly belongs to the MAIN agent.
83
+ *
84
+ * `dsh-subagent` marks a delegated child with `origin: 'subagent'` and a non-zero `delegationDepth`;
85
+ * either is enough to leave it alone. An unknown shape is "cannot tell", and "cannot tell" counts as
86
+ * MAIN: the switch is the user's instruction about their own session, and a shape we do not recognize
87
+ * is not evidence that we are looking at a child.
88
+ */
89
+ export declare function isMainAgent(context: AssembleContext): boolean;
90
+ /**
91
+ * The freeze key for one assembly: the agent's session object, taken structurally because `dsh-agent`
92
+ * is not a peer (`assembleContextFor` passes the agent as both `agent` and `scope`). `undefined` means
93
+ * the assembly is not tied to a session, and then the text is read live.
94
+ */
95
+ export declare function sessionKeyOf(context: AssembleContext): object | undefined;
96
+ export declare function apply(ctx: Context, config: Config): Promise<void>;
97
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC5D,OAAO,EAA0B,KAAK,eAAe,EAAuB,MAAM,gCAAgC,CAAA;AAClH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAaxC,wFAAwF;AACxF,eAAO,MAAM,IAAI,oBAAoB,CAAA;AAErC;;;;GAIG;AACH,eAAO,MAAM,MAAM,UAAmB,CAAA;AAEtC,+EAA+E;AAC/E,eAAO,MAAM,WAAW,oBAAoB,CAAA;AAC5C,wHAAwH;AACxH,eAAO,MAAM,wBAAwB,qBAAqB,CAAA;AAC1D,8GAA8G;AAC9G,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAAuD,CAAA;AACjG,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAQ,CAAA;AAEtC,MAAM,WAAW,MAAM;IACrB,uGAAuG;IACvG,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAA;IACvC,iHAAiH;IACjH,WAAW,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAA;IAC3C,6GAA6G;IAC7G,YAAY,CAAC,EAAE,QAAQ,CAAC,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC,CAAA;IACtD,qEAAqE;IACrE,IAAI,CAAC,EAAE,QAAQ,CAAC,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;IAC9C,mGAAmG;IACnG,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACvC,4HAA4H;IAC5H,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACtC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACvC,0FAA0F;IAC1F,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAC3C,wFAAwF;IACxF,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;CAC5C;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;aAUjB,CAAA;AAQF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAM7D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAIzE;AAiED,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgHvE"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * This plugin's consumption of the base's runtime interface gate.
3
+ *
4
+ * The DECISION lives in the base — `checkInterface` + `readInterfaceRequirement`, members of the v1
5
+ * `.` surface (root `AGENTS.md`: knowledge that CAN be fixed by one base release lives in the base).
6
+ * What cannot live there is the one question this file answers: "does the base I loaded even HAVE the
7
+ * gate?" A base too old to carry it must not stop the plugin from mounting, so the fallback belongs
8
+ * to the consumer, per the family's rule for knowledge that must survive an old base.
9
+ *
10
+ * Nothing here throws. The startup caller turns every non-`ok` verdict into one WARNING; an
11
+ * `incompatible` verdict additionally withholds the base's shared capabilities — the prompt layer
12
+ * uses this plugin's own built-in defaults, the compatibility gate is skipped and provisioning takes
13
+ * the legacy path — but the plugin ALWAYS mounts (tools, service, Remote and UI stay registered).
14
+ * "Cannot tell" is never "incompatible": it warns and uses the base normally.
15
+ *
16
+ * **This file is a SHARED SHIM and must stay byte-identical in BOTH plugins** — only the `@module`
17
+ * line below differs. Everything here is the consumption contract and the ONE verdict→decision
18
+ * mapping; nothing in it is plugin-specific by construction. What IS plugin-specific (which text the
19
+ * prompt layer falls back to, which resources take the legacy path) belongs in that plugin's own
20
+ * `envinit.ts`, never here. `base/plugin-base/test/interface_consumers.spec.ts` pins the two copies
21
+ * against each other, so a divergence is a red test instead of two shims that drifted.
22
+ *
23
+ * The loader SKELETON the two startups share lives here too: the once-per-process cache
24
+ * ({@link createLoadCache}), the guard that turns any throw into one warning
25
+ * ({@link loadGuarded}), and the gate→decision step ({@link gateOrDegrade}). Those sentences are
26
+ * observably NOT the same in the two plugins, so every shared function takes them as
27
+ * {@link DegradeWords}: the shim owns the mechanism, the caller owns the wording, and the two trees'
28
+ * `envinit.spec.ts` pin each plugin's own text verbatim.
29
+ *
30
+ * @module @avantf/dsh-identity/interface_gate
31
+ */
32
+ import type { InterfaceVerdict } from '@avantf/dsh-plugin-base';
33
+ /** The slice of the loaded base this consumer reads; every member is optional and read guarded. */
34
+ export interface InterfaceGateModule {
35
+ readonly INTERFACE_VERSION?: unknown;
36
+ readonly checkInterface?: unknown;
37
+ readonly readInterfaceRequirement?: unknown;
38
+ }
39
+ /**
40
+ * Whether the base's shared capabilities may be used under `verdict`.
41
+ *
42
+ * The ONE mapping from the gate's verdict to the loader's decision, exported so it is asserted
43
+ * directly and the loader and its test cannot drift: only `incompatible` withholds the base;
44
+ * `cannot-tell` does not, because it is not a negative answer. A `false` here is still a DEGRADATION
45
+ * (own prompt defaults, gate skipped, legacy provisioning), never a refused mount.
46
+ */
47
+ export declare function baseIsUsable(verdict: InterfaceVerdict): boolean;
48
+ /**
49
+ * Ask the loaded base whether it implements the interface generation this artifact was built for.
50
+ *
51
+ * @param module - the loaded base module (only `checkInterface` / `readInterfaceRequirement` are
52
+ * touched, each through a guarded read).
53
+ * @param bakedUrl - test seam: the bake record to read; production leaves it undefined.
54
+ * @returns a verdict that is never `incompatible` unless BOTH sides were readable and differ, and
55
+ * never a throw.
56
+ */
57
+ export declare function interfaceVerdict(module: unknown, bakedUrl?: URL | string): InterfaceVerdict;
58
+ /** The logging surface the shared loader helpers need; both plugins' loggers satisfy it. */
59
+ export interface GateLogger {
60
+ warn(message: string): void;
61
+ }
62
+ /**
63
+ * The two sentences the shared loader may NOT own, because the plugins word them differently: mem
64
+ * says the FRAMEWORK is skipped and names legacy provisioning among the withheld capabilities,
65
+ * mission says the COMPATIBILITY GATE is skipped and names none. Neither is drift — both are read by
66
+ * an operator — so the caller passes them in and the shared code interpolates them verbatim.
67
+ */
68
+ export interface DegradeWords {
69
+ /** This module's warning token, so one grep finds every startup line: `envinit:` / `compat:`. */
70
+ readonly prefix: string;
71
+ /** The tail after "environment initialisation failed (...); " in the guarded-load failure line. */
72
+ readonly loadFailure: string;
73
+ /** The parenthetical after "the base's shared capabilities are NOT used (" in the incompatible line. */
74
+ readonly withheld: string;
75
+ }
76
+ /**
77
+ * Run the runtime interface gate and answer whether the loaded base's shared capabilities may be
78
+ * used.
79
+ *
80
+ * `false` is a DEGRADATION the caller answers by taking its own full-degradation route (no runtime,
81
+ * own prompt defaults, the compatibility gate skipped, legacy provisioning) — never a refused mount,
82
+ * and never a throw. `cannot-tell` warns and answers `true`: "cannot tell" is not a negative answer,
83
+ * so the base is used normally. The verdict's own reason is the base's; only the surrounding sentence
84
+ * is the caller's ({@link DegradeWords}).
85
+ */
86
+ export declare function gateOrDegrade(module: unknown, log: GateLogger | undefined, words: DegradeWords): boolean;
87
+ /**
88
+ * The guard that makes a loader's "never throws" true: every failure is one warning plus
89
+ * `undefined`. The caller owns the sentence's tail (`words`) and its own cache discipline — a
90
+ * FAILED load is not cached, so this must return `undefined` rather than reject (see
91
+ * {@link createLoadCache}).
92
+ */
93
+ export declare function loadGuarded<O, T>(options: O, loadOnce: (options: O) => Promise<T | undefined>, log: GateLogger | undefined, words: DegradeWords): Promise<T | undefined>;
94
+ /** The per-process result of a guarded load: `load` is idempotent, `clear` is for disposal. */
95
+ export interface LoadCache<O, T> {
96
+ load(options: O): Promise<T | undefined>;
97
+ /** Drop the result and the in-flight promise, so the next `load` runs again (unmount/reload). */
98
+ clear(): void;
99
+ }
100
+ /**
101
+ * Wrap a guarded loader in the cache discipline both startups share: the first call runs it, every
102
+ * later and concurrent call shares its result, and a FAILED load is NOT cached. A failure is "cannot
103
+ * tell", not a verdict — and it may be transient (offline at boot, a half-written install) — so a
104
+ * profile reload, or a second instance in the same process, retries instead of replaying the first
105
+ * failure for the whole process lifetime.
106
+ */
107
+ export declare function createLoadCache<O, T>(guarded: (options: O) => Promise<T | undefined>): LoadCache<O, T>;
108
+ /**
109
+ * Whether a Cordis context is still alive.
110
+ *
111
+ * Cordis clears `fiber.uid` on disposal, and every later context call then throws
112
+ * `INACTIVE_EFFECT`. `apply` awaits environment preparation, so a profile reload or unload can land
113
+ * inside that window and half-register a plugin (the Remote face cannot be rolled back). A fiberless
114
+ * stub counts as active: an unknown shape is "cannot tell", never "disposed".
115
+ *
116
+ * Generic Cordis liveness rather than interface-gate knowledge — but the two plugins' startup paths
117
+ * share it byte-for-byte, so it lives with their one shared shim, where the two readings of "is my
118
+ * context alive" cannot drift.
119
+ */
120
+ export declare function stillActive(ctx: unknown): boolean;
121
+ //# sourceMappingURL=interface_gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface_gate.d.ts","sourceRoot":"","sources":["../../src/interface_gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE/D,mGAAmG;AACnG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IACpC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAA;CAC5C;AAkBD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAE/D;AA8BD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,MAAM,GAAG,gBAAgB,CAuB3F;AAED,4FAA4F;AAC5F,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAeD;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,iGAAiG;IACjG,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,mGAAmG;IACnG,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,wGAAwG;IACxG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,GAAG,SAAS,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAiBxG;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,CAAC,EACpC,OAAO,EAAE,CAAC,EACV,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,EAChD,GAAG,EAAE,UAAU,GAAG,SAAS,EAC3B,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAUxB;AAED,+FAA+F;AAC/F,MAAM,WAAW,SAAS,CAAC,CAAC,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IACxC,iGAAiG;IACjG,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAyBtG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGjD"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The plugin's log sink: `ctx.logger` never reaches the terminal, so lines also go to
3
+ * stderr (`console.error`, leaving stdout free) and stay mirrored into the host logger.
4
+ * @module @avantf/dsh-identity/log
5
+ */
6
+ export declare const LOG_PREFIX = "[avantf-identity]";
7
+ export interface IdentityLogger {
8
+ info(message: string): void;
9
+ warn(message: string): void;
10
+ error(message: string): void;
11
+ }
12
+ /** Build the plugin's logger; `sink` is injectable so a test can assert the lines. */
13
+ export declare function createLogger(host: {
14
+ info(message: string): void;
15
+ warn(message: string): void;
16
+ error(message: string): void;
17
+ } | undefined, sink?: (line: string) => void): IdentityLogger;
18
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/log.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,UAAU,sBAAsB,CAAA;AAE7C,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAC1B,IAAI,EAAE;IAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,SAAS,EAC5G,IAAI,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAwC,GAC/D,cAAc,CAchB"}
@@ -0,0 +1,45 @@
1
+ /** The three files that make up one identity, in render order. */
2
+ export declare const IDENTITY_FILES: readonly ["IDENTITY", "SOUL", "RULES"];
3
+ /** One of {@link IDENTITY_FILES}. */
4
+ export type IdentityFileName = (typeof IDENTITY_FILES)[number];
5
+ /** The locales the family ships presets in (`dsh-client-locale`'s published `LOCALE_IDS`). */
6
+ export declare const LOCALE_IDS: readonly ["zh", "en"];
7
+ /** The locale a preset falls back to when the requested one has no directory. */
8
+ export declare const FALLBACK_LOCALE = "en";
9
+ /** `coder` / `assistant` / `analyst` — a NEW preset id is rejected unless it matches this. */
10
+ export declare const PRESET_ID_PATTERN: RegExp;
11
+ /** The data-home input shape, mirrored from the base so {@link resolveDataHomeMirror} takes named slots. */
12
+ export interface DataHomeInput {
13
+ /** Layer ⑤: an explicit caller value; blank or omitted means "not given". */
14
+ readonly explicit?: string;
15
+ /** Layer ④: the environment map; defaults to `process.env`. */
16
+ readonly env?: Record<string, string | undefined>;
17
+ /** Layer ②: the configured `common.dataHome`; blank or omitted means "not configured". */
18
+ readonly configured?: string;
19
+ }
20
+ /** `~/x` → `<home>/x`, `~` → `<home>`; anything else is used as given. Mirrors the base's `expandHome`. */
21
+ export declare function expandHomeMirror(path: string, home?: string): string;
22
+ /**
23
+ * The data root, with the base's exact layer order and named slots.
24
+ * @see module note — this is a mirror, pinned against the linked base by `test/paths.spec.ts`.
25
+ */
26
+ export declare function resolveDataHomeMirror(input?: DataHomeInput): string;
27
+ /** `<data home>/identity` — the whole identity tree for this data home. */
28
+ export declare function identityRoot(dataHome: string): string;
29
+ /** `<identity root>/profiles/<profile>` — the effective identity. */
30
+ export declare function profileDir(root: string, profile: string): string;
31
+ /** `<identity root>/presets` — the preset library. */
32
+ export declare function presetsRoot(root: string): string;
33
+ /**
34
+ * `<identity root>/profiles/<profile>/.edits` — this profile's own versions of identities it has
35
+ * edited. A subdirectory of the profile so the three effective files stay the profile's only FLAT
36
+ * files (the reader only ever looks for `IDENTITY/SOUL/RULES.md`, and the prompt path is unchanged).
37
+ */
38
+ export declare function profileEditsRoot(root: string, profile: string): string;
39
+ /** `<identity root>/profiles/<profile>/.edits/<id>` — this profile's version of identity `id`. */
40
+ export declare function profileEditDir(root: string, profile: string, id: string): string;
41
+ /** `<identity root>/presets/<id>/<locale>`. */
42
+ export declare function presetLocaleDir(root: string, id: string, locale: string): string;
43
+ /** `<identity root>/.provisioned` — the one-time built-in release marker. */
44
+ export declare function provisionMarker(root: string): string;
45
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/paths.ts"],"names":[],"mappings":"AA6BA,kEAAkE;AAClE,eAAO,MAAM,cAAc,wCAAyC,CAAA;AAEpE,qCAAqC;AACrC,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9D,8FAA8F;AAC9F,eAAO,MAAM,UAAU,uBAAwB,CAAA;AAE/C,iFAAiF;AACjF,eAAO,MAAM,eAAe,OAAO,CAAA;AAEnC,8FAA8F;AAC9F,eAAO,MAAM,iBAAiB,QAAqC,CAAA;AAEnE,4GAA4G;AAC5G,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACjD,0FAA0F;IAC1F,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,2GAA2G;AAC3G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAG/E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,GAAE,aAAkB,GAAG,MAAM,CAOvE;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,kGAAkG;AAClG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,+CAA+C;AAC/C,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,201 @@
1
+ import type { IdentityLogger } from './log.js';
2
+ import { type IdentityFileName } from './paths.js';
3
+ /** One preset, as the settings page lists it. */
4
+ export interface PresetSummary {
5
+ readonly id: string;
6
+ readonly locales: readonly string[];
7
+ }
8
+ /** One preset file's text. */
9
+ export interface PresetFileText {
10
+ readonly name: IdentityFileName;
11
+ readonly file: string;
12
+ readonly text: string;
13
+ readonly present: boolean;
14
+ readonly bytes: number;
15
+ }
16
+ /** A read preset: the locale that answered, and whether the requested one had to fall back. */
17
+ export interface PresetRead {
18
+ readonly id: string;
19
+ readonly requestedLocale: string;
20
+ readonly resolvedLocale: string;
21
+ readonly fellBack: boolean;
22
+ readonly files: readonly PresetFileText[];
23
+ }
24
+ /**
25
+ * The built-in preset a profile with NO identity is seeded from.
26
+ *
27
+ * `assistant` is the neutral one (`analyst` / `coder` are specific roles); the two specific ones are a
28
+ * deliberate choice by the user, never a default.
29
+ */
30
+ export declare const DEFAULT_PRESET_ID = "assistant";
31
+ /** What {@link seedProfileIfEmpty} did: whether it wrote anything, from which preset, and why it did not. */
32
+ export interface ProfileSeed {
33
+ readonly seeded: boolean;
34
+ readonly preset?: string;
35
+ readonly resolvedLocale?: string;
36
+ readonly fellBack?: boolean;
37
+ readonly written: readonly string[];
38
+ readonly error?: string;
39
+ }
40
+ /** Whether `id` is a preset id this plugin will use as a directory name. */
41
+ export declare function isValidPresetId(id: string): boolean;
42
+ /** Whether `locale` is a locale this plugin will use as a directory name. */
43
+ export declare function isValidLocale(locale: string): boolean;
44
+ /**
45
+ * Where the built-in preset resources live at runtime.
46
+ *
47
+ * Both candidates are real, for different builds: the host entry is BUNDLED into `lib/index.js`, so
48
+ * `./assets/presets` finds `lib/assets/presets` (the only location that ships); under `vitest` the
49
+ * module is `src/…`, so `../assets/presets` finds the source tree's `assets/presets`.
50
+ */
51
+ export declare function resolveAssetsDir(from?: string): string | undefined;
52
+ /** The preset ids the shipped resource tree carries, in name order. */
53
+ export declare function packagedPresetIds(assetsDir: string): string[];
54
+ /**
55
+ * Release the built-in presets into `<identity root>/presets/`, MISSING-ONLY.
56
+ *
57
+ * A preset directory that already exists is left completely alone — that single rule covers both
58
+ * "never overwrite an edit" and "do not resurrect a preset the user deleted". The `.provisioned`
59
+ * marker then records which release ran, so a later release only has to add preset directories that
60
+ * are genuinely new.
61
+ */
62
+ export declare function materializePresets(options: {
63
+ readonly assetsDir: string;
64
+ readonly root: string;
65
+ readonly version: string;
66
+ readonly logger?: IdentityLogger;
67
+ }): {
68
+ readonly copied: readonly string[];
69
+ readonly skipped: readonly string[];
70
+ };
71
+ /**
72
+ * The preset ids a previous release recorded.
73
+ *
74
+ * `[]` means "no marker at all" — a genuine first run, where nothing has been released yet. `undefined`
75
+ * means a marker EXISTS but cannot be used (unreadable, not JSON, no `presets` array): which ids it
76
+ * recorded is UNKNOWN, and the caller must not treat that as "nothing was released" — copying then
77
+ * would resurrect a preset the user deleted, which `identity/README.md` promises never happens.
78
+ */
79
+ export declare function readReleasedPresets(marker: string): string[] | undefined;
80
+ /** Every preset on disk, with the locales each has a directory for. */
81
+ export declare function listPresets(root: string): PresetSummary[];
82
+ /** Read one preset's three files, falling back to English when the requested locale is absent. */
83
+ export declare function readPreset(root: string, id: string, requestedLocale: string): PresetRead | undefined;
84
+ /** Write one preset file atomically; the caller decides whether the locale exists. */
85
+ export declare function writePresetFile(root: string, id: string, locale: string, name: IdentityFileName, text: string): void;
86
+ /** Remove one preset directory entirely. */
87
+ export declare function deletePreset(root: string, id: string): boolean;
88
+ /**
89
+ * Apply a preset to the effective identity: copy its resolved-locale files into
90
+ * `profiles/<profile>/`, OVERWRITING (the UI owns the second confirmation).
91
+ *
92
+ * A file the preset does not carry is left as it is rather than blanked: applying a preset is "take
93
+ * this text", never "delete what this preset happens not to mention".
94
+ */
95
+ export declare function applyPresetToProfile(options: {
96
+ readonly root: string;
97
+ readonly profile: string;
98
+ readonly id: string;
99
+ readonly locale: string;
100
+ }): {
101
+ readonly resolvedLocale: string;
102
+ readonly fellBack: boolean;
103
+ readonly written: readonly string[];
104
+ readonly missing: readonly string[];
105
+ } | undefined;
106
+ /**
107
+ * Give `profiles/<profile>/` an identity to start from, when it has none.
108
+ *
109
+ * The switch's first enable asks for this. The first run materializes the PRESET LIBRARY only — it never
110
+ * writes an effective identity — so a profile that was never given files has nothing to replace the
111
+ * native prompt with: enabling the switch would leave the prompt untouched and the three editors empty.
112
+ *
113
+ * NEVER overwrites: any one non-empty file means the user has an identity, and nothing is written. The
114
+ * built-in {@link DEFAULT_PRESET_ID} is preferred; when the user has deleted it, the first preset still
115
+ * in the library stands in. An empty library is REPORTED (the caller surfaces the reason) rather than
116
+ * invented — this function does not know what the user's identity should say.
117
+ */
118
+ export declare function seedProfileIfEmpty(options: {
119
+ readonly root: string;
120
+ readonly profile: string;
121
+ readonly locale: string;
122
+ }): ProfileSeed;
123
+ /**
124
+ * The identity edits a profile owns: `<profiles/<profile>>/.edits/<id>/`.
125
+ *
126
+ * WHY THIS EXISTS. `applyPresetToProfile` overwrites the effective identity, so without a copy the
127
+ * user's own text for the identity they were on would be destroyed by switching to another one — and
128
+ * switching back would hand them the preset's factory text instead of what they wrote. The edit store
129
+ * is what makes the switch REVERSIBLE: leaving an identity files its current content away under that id,
130
+ * and coming back to the id restores that content in preference to the library's.
131
+ *
132
+ * It is per PROFILE on purpose: an edit is one person's version of an identity, so it must not leak into
133
+ * another profile, and the library keeps the pristine text (「恢复预设原文」drops the edit).
134
+ */
135
+ /** Whether this profile has its own version of `id` — the directory exists and carries any text. */
136
+ export declare function readProfileEdit(options: {
137
+ readonly root: string;
138
+ readonly profile: string;
139
+ readonly id: string;
140
+ }): {
141
+ readonly files: readonly PresetFileText[];
142
+ readonly bytes: number;
143
+ } | undefined;
144
+ /**
145
+ * File the CURRENT effective identity away as this profile's version of `id`.
146
+ *
147
+ * Called when the user leaves `id` for another identity. An empty effective identity files nothing (an
148
+ * empty version would shadow the library's text on the way back for no reason) — the edit directory is
149
+ * then left as it was. Nothing else is touched: the effective files stay where they are until the caller
150
+ * applies the next identity.
151
+ */
152
+ export declare function snapshotProfileEdit(options: {
153
+ readonly root: string;
154
+ readonly profile: string;
155
+ readonly id: string;
156
+ /** The effective files, already read (in render order) by a caller that just needed them. */
157
+ readonly files?: readonly PresetFileText[];
158
+ }): {
159
+ readonly written: readonly string[];
160
+ } | undefined;
161
+ /** Write this profile's version of `id` into the effective identity (overwriting the effective files). */
162
+ export declare function applyProfileEditToProfile(options: {
163
+ readonly root: string;
164
+ readonly profile: string;
165
+ readonly id: string;
166
+ }): {
167
+ readonly written: readonly string[];
168
+ } | undefined;
169
+ /** Remove this profile's version of `id`, so `id` falls back to the library's text. */
170
+ export declare function dropProfileEdit(options: {
171
+ readonly root: string;
172
+ readonly profile: string;
173
+ readonly id: string;
174
+ }): boolean;
175
+ /**
176
+ * Whether the EFFECTIVE identity differs from the library preset's text — "you have your own version in
177
+ * force". Computed from the two texts rather than from the edit store existing, because a hand-edited
178
+ * identity has no stored version yet and still must be restorable.
179
+ *
180
+ * Every locale the preset ships counts as a match, not just the one the page is showing: a preset applied
181
+ * in `zh` and then read with an `en` UI is NOT a local edit, and reporting it as one would offer a
182
+ * destructive 「restore」 that silently switched the identity's language.
183
+ */
184
+ export declare function profileDiffersFromPreset(options: {
185
+ readonly root: string;
186
+ readonly profile: string;
187
+ readonly id: string;
188
+ readonly locale: string;
189
+ /** The effective files, already read (in render order) by a caller that just read them. */
190
+ readonly effectiveFiles?: readonly PresetFileText[];
191
+ }): boolean;
192
+ /** Save the effective identity as a new preset under `<locale>`. Refuses an id that already exists. */
193
+ export declare function saveProfileAsPreset(options: {
194
+ readonly root: string;
195
+ readonly profile: string;
196
+ readonly id: string;
197
+ readonly locale: string;
198
+ }): {
199
+ readonly written: readonly string[];
200
+ } | undefined;
201
+ //# sourceMappingURL=presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/presets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The identity a session starts with is the identity that session keeps.
3
+ *
4
+ * WHY. The system prompt is assembled before EVERY model step, and this plugin's section text is read
5
+ * from disk at that moment. Without a freeze, editing the files (or applying a preset) mid-session
6
+ * rewrites the prompt of a conversation that is already running: the persona changes under the reader,
7
+ * and the request prefix changes with it, so the provider's prompt cache is thrown away for that
8
+ * session. Neither is what a user means by "apply this identity" — they mean the next conversation.
9
+ *
10
+ * HOW. The assembly's `AssembleContext.scope` is the agent and `context.agent.session` is the session
11
+ * object (`assembleContextFor` in `dsh-agent` sets both); the session object is stable for the life of a
12
+ * session and different for the next one. Keying a `WeakMap` by it freezes per session without a
13
+ * lifetime to manage: entries disappear with the session they belong to, so a long-running host cannot
14
+ * accumulate them.
15
+ *
16
+ * An assembly with no session object (a bare probe or a unit fixture) is NOT frozen — it reads live, so
17
+ * nothing depends on an identity that was never there.
18
+ *
19
+ * @module @avantf/dsh-identity/session-freeze
20
+ */
21
+ /** One frozen string per session object. */
22
+ export declare class SessionFreeze {
23
+ private readonly held;
24
+ /**
25
+ * The frozen text for `key`, calling `read` exactly once per key.
26
+ *
27
+ * @param key - the session object, or `undefined` to read live on every call.
28
+ * @param read - reads the current text; called only when no value is held yet.
29
+ * @returns the text this session is stuck with.
30
+ */
31
+ text(key: object | undefined, read: () => string): string;
32
+ }
33
+ //# sourceMappingURL=session-freeze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-freeze.d.ts","sourceRoot":"","sources":["../../src/session-freeze.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,4CAA4C;AAC5C,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;IAErD;;;;;;OAMG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,MAAM,GAAG,MAAM;CAU1D"}