@hasna-internal/kai-fs-observation-policy 0.1.1-rc.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/fs/fs-observation-policy/README.md
5
+ README.md: a126c7d0e3c806e3ce22eedfb05b21d8fd5aba60
6
+ README.zh.md: 708eea6ca3134a1870b41b7b3dec3f95d1cc6b2b
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # @hasna-internal/kai-fs-observation-policy
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ The **fs-observation-policy plugin**: it records observed presence or absence and adds read-before-edit plus guarded write/edit on top of the `ctx.fs` provider contract ([`@hasna-internal/kai-fs`](../fs)) — through the `fs/*` event gate, **NOT** through a method service. This plugin registers **no** `ctx.fsPolicy` service and has no public `read`/`write`/`edit`/`resolve` methods. It is the policy third of the filesystem stack: not a swappable seam, but the policy that does not belong on the `FileSystem` provider base class.
6
+
7
+ ```ts
8
+ import type { Context } from '@deepseek-ai/cordis'
9
+ import * as FsPolicy from '@hasna-internal/kai-fs-observation-policy'
10
+
11
+ declare const ctx: Context
12
+
13
+ // No service to inject — this plugin only registers the three fs/* listeners.
14
+ // Load it alongside a ctx.fs provider (e.g. @hasna-internal/kai-fs-local) and the
15
+ // @hasna-internal/kai-tool-fs tools; the tools dispatch the fs/* events this plugin
16
+ // decides. Order does not matter for resolution (no inject), but the policy
17
+ // listener should be the first decider registered for the fs/*-intent slots.
18
+ await ctx.plugin(FsPolicy)
19
+ ```
20
+
21
+ ## The four-layer split
22
+
23
+ | Layer | Package | Role |
24
+ |---|---|---|
25
+ | tool / executor | `@hasna-internal/kai-tool-fs` | model-facing schemas + read windowing + text rendering; reads/writes/edits via `ctx.fs`, dispatches the `fs/*` events |
26
+ | policy | `@hasna-internal/kai-fs-observation-policy` (this) | observed-state + read-before-edit + version-guarded write/edit, contributed through the `fs/*` event gate (no service) |
27
+ | provider contract | `@hasna-internal/kai-fs` | `ctx.fs`: text IO + atomic mutation primitives (optional version guard); owns the `fs/*` event vocabulary |
28
+ | provider | `@hasna-internal/kai-fs-local` | local implementation of `ctx.fs` |
29
+
30
+ ## How the gate participates
31
+
32
+ Three `fs/*` events (declared by `@hasna-internal/kai-fs`, dispatched by `@hasna-internal/kai-tool-fs`):
33
+
34
+ | Event | This plugin's listener |
35
+ |---|---|
36
+ | `fs/write-intent` | Unseen or observed absent → `{ kind: 'createIfAbsent' }`; observed present → `{ kind: 'replaceIfVersion', version: vObserved }`. Single-slot decision; does NOT call `next()`. |
37
+ | `fs/edit-intent` | Unseen → `FS_NOT_OBSERVED`; observed absent → `FS_NOT_FOUND`; observed present → `{ version: vObserved }` as the CAS basis. Single-slot decision; does NOT call `next()`. |
38
+ | `fs/observed` | Records `{ kind: 'present', version }` or `{ kind: 'absent' }` for this owner+target. Synchronous, side-effect-only `WeakMap.set`. |
39
+
40
+ ## Observed state is the prior-observation record; freshness is provider CAS
41
+
42
+ Observed state is a weak owner-to-target map with three logical states: unseen, confirmed absent, or present at a version. A successful file read or mutation records presence; a metadata miss from `read` or the `str_replace_editor` `view`, `str_replace`, or `insert` command records absence before returning `FS_NOT_FOUND`. The plugin performs no filesystem I/O: it converts that state into a provider guard. Presence supplies the observed version, while absence lets only a `createIfAbsent` write proceed; edit has no version basis and returns `FS_NOT_FOUND`. A windowed read observes the whole file version, so a later targeted edit is allowed only while that file remains unchanged. State is discarded on plugin disposal and is not persisted across sessions.
43
+
44
+ ## Single-slot, first-wins
45
+
46
+ The `fs/write-intent`/`fs/edit-intent` slots hold exactly one decider — this plugin fully decides and does not call `next()`. The slot is first-wins by registration order; this plugin owning it is the default-deployment convention, not an event-enforced invariant (a decider registered before / `prepend`ed would win instead). This is not a composable authorization chain — layered permission/audit/sandbox interception belongs on `tools/execute`.
47
+
48
+ ## No method coupling
49
+
50
+ Because the plugin influences the world only through events, removing it does not break `@hasna-internal/kai-tool-fs` at a service-injection boundary: the tool falls through to the bare `ctx.fs` provider (unconditional write/edit, no observed-state). Loading it back layers the policy on. That graceful add/remove is the whole point of the event gate over a mandatory method service.
51
+
52
+ ## Model Experience
53
+
54
+ ### Filesystem tool outcome
55
+
56
+ #### What the model sees
57
+
58
+ This plugin adds no prompt or schema. It rejects an edit without a prior observation with code `FS_NOT_OBSERVED` and exact message `edit requires reading "<path>" first`; editing a target just observed absent returns `FS_NOT_FOUND`. Guarded mutations whose positive observation is stale propagate the provider-owned `FS_STALE_VERSION` error. [`dsh-tool-fs`](../tool-fs/README.md) owns the model-facing error wrapper, which appends the recovery instruction to `FS_STALE_VERSION` (`— re-read the file, then retry`) and `FS_NOT_OBSERVED` (`— read the file, then retry`) messages while preserving the code. Following the stale remedy on an externally deleted target now records absence: the next guarded write may recreate it with `createIfAbsent`, while the provider atomically preserves any concurrent creator.
59
+
60
+ #### Token effect
61
+
62
+ Zero tokens on allowed operations beyond the ordinary tool result. A denial adds the small retained error result and avoids any success payload.
63
+
64
+ #### KV Cache effect
65
+
66
+ Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
67
+
68
+ ## Known Limitations and Deferred Work
69
+
70
+ - **Observed state does not survive a session resume** — persistence of the `WeakMap` record is deferred, so a resumed session must re-read files before guarded writes/edits.
71
+ - **Actors without an agent session can never satisfy the policy** — their edits throw `FS_NOT_OBSERVED` and their writes always resolve `createIfAbsent`, so a non-agent caller cannot overwrite an existing file through the gate.
72
+ - **Direct `ctx.fs` reads emit no `fs/observed`** — a file read outside the `read` tool stays unobserved, and a later guarded edit rejects with `FS_NOT_OBSERVED` until the tool reads it.
73
+ - **Authorization is version freshness, not view completeness** — any windowed read authorizes a full-file overwrite of an unchanged file, deliberately weaker than a full-view rule ([seam-split Agent Note](../../../.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.md)).
package/README.zh.md ADDED
@@ -0,0 +1,73 @@
1
+ # @hasna-internal/kai-fs-observation-policy
2
+
3
+ [English](README.md) | 中文
4
+
5
+ **fs-observation-policy 插件**:它记录观测到的存在或缺失状态,并在 `ctx.fs` 提供方约定([`@hasna-internal/kai-fs`](../fs))之上增加编辑前读取和带防护的写入/编辑;它通过 `fs/*` 事件门禁参与,**不是**通过方法服务。该插件**不**注册 `ctx.fsPolicy` 服务,也没有公开的 `read`/`write`/`edit`/`resolve` 方法。它是文件系统栈的政策层:不是可替换 seam,而是不应位于 `FileSystem` 提供方基类上的政策。
6
+
7
+ ```ts
8
+ import type { Context } from '@deepseek-ai/cordis'
9
+ import * as FsPolicy from '@hasna-internal/kai-fs-observation-policy'
10
+
11
+ declare const ctx: Context
12
+
13
+ // No service to inject — this plugin only registers the three fs/* listeners.
14
+ // Load it alongside a ctx.fs provider (e.g. @hasna-internal/kai-fs-local) and the
15
+ // @hasna-internal/kai-tool-fs tools; the tools dispatch the fs/* events this plugin
16
+ // decides. Order does not matter for resolution (no inject), but the policy
17
+ // listener should be the first decider registered for the fs/*-intent slots.
18
+ await ctx.plugin(FsPolicy)
19
+ ```
20
+
21
+ ## 四层拆分
22
+
23
+ | 层 | 包 | 角色 |
24
+ |---|---|---|
25
+ | 工具/执行器 | `@hasna-internal/kai-tool-fs` | 面向模型的 schema、读取窗口和文本渲染;通过 `ctx.fs` 读取/写入/编辑,并分派 `fs/*` 事件 |
26
+ | 策略 | `@hasna-internal/kai-fs-observation-policy`(本包) | 通过 `fs/*` 事件门禁提供已观察状态、编辑前读取和版本防护的写入/编辑(无服务) |
27
+ | 提供方约定 | `@hasna-internal/kai-fs` | `ctx.fs`:文本 I/O 与原子变更原语(可选版本防护);拥有 `fs/*` 事件词汇 |
28
+ | 提供方 | `@hasna-internal/kai-fs-local` | `ctx.fs` 的本地实现 |
29
+
30
+ ## 门禁的参与方式
31
+
32
+ 三个 `fs/*` 事件(由 `@hasna-internal/kai-fs` 声明,`@hasna-internal/kai-tool-fs` 分派):
33
+
34
+ | 事件 | 本插件的监听器 |
35
+ |---|---|
36
+ | `fs/write-intent` | 未见或已观测为缺失 → `{ kind: 'createIfAbsent' }`;已观测为存在 → `{ kind: 'replaceIfVersion', version: vObserved }`。单 slot 决策;不调用 `next()`。 |
37
+ | `fs/edit-intent` | 未见 → `FS_NOT_OBSERVED`;已观测为缺失 → `FS_NOT_FOUND`;已观测为存在 → 返回 `{ version: vObserved }` 作为 CAS 基础。单 slot 决策;不调用 `next()`。 |
38
+ | `fs/observed` | 为该所有者与目标记录 `{ kind: 'present', version }` 或 `{ kind: 'absent' }`。同步、只有副作用的 `WeakMap.set`。 |
39
+
40
+ ## 已观察状态是先前观察记录;新鲜度由提供方 CAS 保证
41
+
42
+ 观测状态是一张以所有者为弱键、记录各目标的映射表,具有三种逻辑状态:未见、确认缺失、存在于某个版本。成功读取文件或变更会记录存在;`read` 的元数据未命中,或 `str_replace_editor` 的 `view`、`str_replace`、`insert` 命令发生元数据未命中时,都会在返回 `FS_NOT_FOUND` 前记录缺失。插件不执行文件系统 I/O:它把该状态转换为提供方防护。存在状态提供观测到的版本;缺失状态只允许 `createIfAbsent` 写入继续,edit 因没有版本基准而返回 `FS_NOT_FOUND`。窗口读取会观察整个文件的版本,因此只有文件保持不变时才允许后续的定向编辑。插件 dispose(资源释放)时会丢弃状态,并且不会跨会话持久化。
43
+
44
+ ## 单 slot、先到者胜
45
+
46
+ `fs/write-intent`/`fs/edit-intent` slot 只容纳一个决策器;本插件会完整决策,不调用 `next()`。slot 按注册顺序先到者胜;由本插件拥有 slot 只是默认部署约定,不是事件强制的不变式(更早注册或通过 `prepend` 注册的决策器会胜出)。这不是可组合的授权链;分层权限/审计/沙箱拦截属于 `tools/execute`。
47
+
48
+ ## 不与方法耦合
49
+
50
+ 由于插件只通过事件影响外部世界,移除它不会在服务注入边界破坏 `@hasna-internal/kai-tool-fs`:工具会直接落到裸 `ctx.fs` 提供方(无条件写入/编辑,无已观察状态)。重新加载插件后,策略会重新生效。相比必需的方法服务,这种可平稳增删的性质正是事件门禁的全部目的。
51
+
52
+ ## 模型体验
53
+
54
+ ### 文件系统工具结果
55
+
56
+ #### 模型看到的内容
57
+
58
+ 该插件不添加提示词或 schema。没有先前观测时,它会以代码 `FS_NOT_OBSERVED` 和精确消息 `edit requires reading "<path>" first` 拒绝编辑;编辑刚被观测为缺失的目标会返回 `FS_NOT_FOUND`。正向观测陈旧时,带防护的变更会传播由提供方拥有的 `FS_STALE_VERSION` 错误。[`dsh-tool-fs`](../tool-fs/README.zh.md) 拥有面向模型的错误包装,会为 `FS_STALE_VERSION` 消息追加恢复指令(`— re-read the file, then retry`)、为 `FS_NOT_OBSERVED` 消息追加恢复指令(`— read the file, then retry`),同时保留错误码。外部删除目标后,遵循陈旧恢复指令会记录缺失:下一次带防护的写入可以通过 `createIfAbsent` 重新创建该目标,而提供方会以原子方式保留任何并发创建者写入的文件。
59
+
60
+ #### Token 影响
61
+
62
+ 允许的操作除了普通工具结果外不增加 token。拒绝会添加少量保留的错误结果,并避免产生成功 payload。
63
+
64
+ #### KV Cache 影响
65
+
66
+ 仅追加;新增可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
67
+
68
+ ## 已知限制与暂缓事项
69
+
70
+ - **已观察状态无法在会话恢复后保留**:`WeakMap` 记录的持久化工作延期处理,因此恢复的会话必须重新读取文件,才能执行防护写入/编辑。
71
+ - **没有 agent(智能体)会话的参与者绝无法满足策略**:它们的编辑会抛出 `FS_NOT_OBSERVED`,写入总会解析为 `createIfAbsent`,因此非 agent 调用方无法通过门禁覆盖现有文件。
72
+ - **直接 `ctx.fs` 读取不会发出 `fs/observed`**:在 `read` 工具之外读取的文件仍未观察;后续防护编辑会以 `FS_NOT_OBSERVED` 拒绝,直到工具读取该文件。
73
+ - **授权依据是版本新鲜度,而非视图完整性**:任何窗口读取都会授权对未变文件执行全文件覆盖,这有意弱于完整视图规则(见 [seam 拆分 Agent Note](../../../.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.zh.md))。
package/lib/index.js ADDED
@@ -0,0 +1,97 @@
1
+ import { FsError } from "@hasna-internal/kai-fs";
2
+ //#region lib/types/index.js
3
+ /**
4
+ * Event-only filesystem observation policy; it registers no service. A weak owner/target map
5
+ * records every authoritative presence/absence observation, single-slot intent listeners derive
6
+ * guards from that state, and the provider performs the atomic freshness/no-clobber check. Without
7
+ * this plugin, tools retain the bare provider's unconditional mutation behavior. See the package
8
+ * README for composition rules.
9
+ * @module @hasna-internal/kai-fs-observation-policy
10
+ */
11
+ /**
12
+ * Per-context observed-file state and the three `fs/*` decisions over it. One
13
+ * instance is created per `apply()` so disposal can drop all state for HMR.
14
+ */
15
+ var ObservedStateGate = class {
16
+ /**
17
+ * Observed-file state, keyed first by the owner object (weakly held, so a
18
+ * collected session frees its state), then by {@link FsTarget.targetKey}. An
19
+ * entry's presence is the prior-observation record; its discriminant keeps
20
+ * confirmed absence distinct from an unseen target.
21
+ */
22
+ observed = /* @__PURE__ */ new WeakMap();
23
+ /**
24
+ * Derive the observed-state owner from the opaque event actor — normally the
25
+ * active agent session. `undefined` when no owner can be derived (e.g. a
26
+ * direct tool call with no agent); such calls read freely but cannot satisfy
27
+ * the write/edit prior-observation policy.
28
+ */
29
+ owner(actor) {
30
+ return actor?.agent?.session;
31
+ }
32
+ get(owner, targetKey) {
33
+ return this.observed.get(owner)?.get(targetKey);
34
+ }
35
+ set(owner, targetKey, observation) {
36
+ let byTarget = this.observed.get(owner);
37
+ if (!byTarget) {
38
+ byTarget = /* @__PURE__ */ new Map();
39
+ this.observed.set(owner, byTarget);
40
+ }
41
+ byTarget.set(targetKey, observation);
42
+ }
43
+ /** Drop all recorded state (HMR safety / disposal). */
44
+ clear() {
45
+ this.observed = /* @__PURE__ */ new WeakMap();
46
+ }
47
+ /**
48
+ * Decide the write intent: unseen or confirmed absent ⇒ `createIfAbsent`;
49
+ * confirmed present ⇒ `replaceIfVersion` at the observed version.
50
+ */
51
+ writeIntent(target, actor) {
52
+ const owner = this.owner(actor);
53
+ const prior = owner ? this.get(owner, target.targetKey) : void 0;
54
+ return prior?.kind === "present" ? {
55
+ kind: "replaceIfVersion",
56
+ version: prior.version
57
+ } : { kind: "createIfAbsent" };
58
+ }
59
+ /**
60
+ * Decide the edit version guard: unseen rejects with `FS_NOT_OBSERVED`,
61
+ * confirmed absence rejects with `FS_NOT_FOUND`, and presence supplies the
62
+ * observed version as the CAS basis.
63
+ */
64
+ editIntent(target, actor) {
65
+ const owner = this.owner(actor);
66
+ const prior = owner ? this.get(owner, target.targetKey) : void 0;
67
+ if (!owner || prior === void 0) throw new FsError(`edit requires reading "${target.displayPath}" first`, "FS_NOT_OBSERVED");
68
+ if (prior.kind === "absent") throw new FsError(`cannot edit "${target.displayPath}": not found`, "FS_NOT_FOUND");
69
+ return { version: prior.version };
70
+ }
71
+ /** Record an authoritative present or absent observation for this owner and target. */
72
+ observe(target, observation, actor) {
73
+ const owner = this.owner(actor);
74
+ if (owner) this.set(owner, target.targetKey, observation);
75
+ }
76
+ };
77
+ /** Cordis plugin name used by loader diagnostics. */
78
+ const name = "fs-observation-policy";
79
+ /**
80
+ * Register the three `fs/*` listeners. No `inject` — this plugin reads no
81
+ * services; it operates only on its own `WeakMap`. The waterfalls are unbound
82
+ * (the tool dispatches them with no `this`), so the listeners take the raw
83
+ * `(target, actor, next)` arguments.
84
+ */
85
+ function apply(ctx) {
86
+ const gate = new ObservedStateGate();
87
+ ctx.effect(() => () => {
88
+ gate.clear();
89
+ }, "fs-observation-policy observed-state teardown");
90
+ ctx.on("fs/write-intent", (target, actor) => Promise.resolve().then(() => gate.writeIntent(target, actor)));
91
+ ctx.on("fs/edit-intent", (target, actor) => Promise.resolve().then(() => gate.editIntent(target, actor)));
92
+ ctx.on("fs/observed", (target, observation, actor) => {
93
+ gate.observe(target, observation, actor);
94
+ });
95
+ }
96
+ //#endregion
97
+ export { apply, name };
@@ -0,0 +1,23 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@hasna-internal/kai-fs-observation-policy`.
4
+ * @module @hasna-internal/kai-fs-observation-policy/invariant
5
+ */
6
+ const PACKAGE_NAME = "@hasna-internal/kai-fs-observation-policy";
7
+ /** Cordis companion plugin name. */
8
+ const name = "fs-observation-policy-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: this package exposes no independent event sequence or mutable data relation
13
+ * beyond contracts enforced at its owning seam.
14
+ */
15
+ const install = () => {};
16
+ /**
17
+ * Register this package's invariant companion.
18
+ * @param ctx - Cordis context carrying the invariant service.
19
+ * @returns the installed registration's disposer after setup succeeds.
20
+ */
21
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
22
+ //#endregion
23
+ export { apply, inject, name };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Event-only filesystem observation policy; it registers no service. A weak owner/target map
3
+ * records every authoritative presence/absence observation, single-slot intent listeners derive
4
+ * guards from that state, and the provider performs the atomic freshness/no-clobber check. Without
5
+ * this plugin, tools retain the bare provider's unconditional mutation behavior. See the package
6
+ * README for composition rules.
7
+ * @module @hasna-internal/kai-fs-observation-policy
8
+ */
9
+ import type { Context } from '@deepseek-ai/cordis';
10
+ export type { FsObservationActor } from './types.ts';
11
+ /** Cordis plugin name used by loader diagnostics. */
12
+ export declare const name = "fs-observation-policy";
13
+ /**
14
+ * Register the three `fs/*` listeners. No `inject` — this plugin reads no
15
+ * services; it operates only on its own `WeakMap`. The waterfalls are unbound
16
+ * (the tool dispatches them with no `this`), so the listeners take the raw
17
+ * `(target, actor, next)` arguments.
18
+ */
19
+ export declare function apply(ctx: Context): void;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@hasna-internal/kai-fs-observation-policy`.
3
+ * @module @hasna-internal/kai-fs-observation-policy/invariant
4
+ */
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "fs-observation-policy-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Vocabulary for the fs-observation-policy plugin: the minimal execution-context
3
+ * fields used to derive an observed-state owner by narrowing the opaque `object`
4
+ * actor the `fs/*` events carry.
5
+ *
6
+ * The provider vocabulary (`FsTarget`, `FsVersion`, write/edit request types) is
7
+ * re-used from `@hasna-internal/kai-fs`; this package owns only the observed-state
8
+ * owner structure on top of it.
9
+ *
10
+ * @module @hasna-internal/kai-fs-observation-policy/types
11
+ */
12
+ /**
13
+ * Minimal structural view of a tool execution the policy plugin needs to derive
14
+ * an observed-state owner. `@hasna-internal/kai-tools`' `ToolExecution` contains
15
+ * these fields, so the tool passes its `exec` straight through as the opaque
16
+ * `object` actor on the `fs/*` events; this plugin narrows that actor to
17
+ * `FsObservationActor` without importing `dsh-tools`, `dsh-agent`, or `dsh-session`.
18
+ *
19
+ * The owner is `agent.session` when present. It is treated as an opaque object
20
+ * identity (a `WeakMap` key); this package never reads any of its fields.
21
+ */
22
+ export interface FsObservationActor {
23
+ /** The agent on whose behalf the call runs, when there is one. */
24
+ agent?: {
25
+ /** The session that owns observed-file state, used as an opaque key. */
26
+ session?: object;
27
+ };
28
+ }
29
+ //# sourceMappingURL=types.d.ts.map
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@hasna-internal/kai-fs-observation-policy",
3
+ "description": "File-context policy plugin for the DeepSeek Harness — observed-state, read-before-edit, and version-guarded write/edit added over the ctx.fs provider seam through the fs/* event gate (no service API)",
4
+ "version": "0.1.1-rc.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/fs/fs-observation-policy"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "lib/index.js",
30
+ "lib/invariant.js",
31
+ "lib/types/**/*.d.ts"
32
+ ],
33
+ "license": "MIT",
34
+ "peerDependencies": {
35
+ "@hasna-internal/kai-fs": "^0.1.1-rc.2",
36
+ "@hasna-internal/kai-invariants": "^0.1.1-rc.2",
37
+ "@deepseek-ai/cordis": "^4.0.1"
38
+ },
39
+ "devDependencies": {
40
+ "@hasna-internal/kai-fs": "^0.1.1-rc.2",
41
+ "@deepseek-ai/cordis": "^4.0.1",
42
+ "@hasna-internal/kai-invariants": "^0.1.1-rc.2",
43
+ "@hasna-internal/kai-llm": "^0.1.1-rc.2"
44
+ }
45
+ }