@hy-sde-org/dsh-internal-urls 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,37 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hy-sde
4
+
5
+ This repository ports the FS-shaped internal-URL system (conflict://, issue://,
6
+ pr:// — one resolver registry behind read/grep/write) from oh-my-pi's
7
+ coding-agent internal-urls and conflict-detect modules, adapted to the
8
+ DeepSeek Harness file-services seam. Ported portions are distributed under the
9
+ same MIT license with their original copyright held by their respective
10
+ authors:
11
+
12
+ Copyright (c) 2025 Mario Zechner
13
+ Copyright (c) 2025-2026 Can Bölük
14
+
15
+ See THIRD-PARTY-NOTICES.md for full provenance per module.
16
+
17
+ Parts of this repository are derived from DeepSeek Harness
18
+ (https://github.com/deepseek-ai/deepseek-harness), MIT License,
19
+ Copyright (c) 2026 DeepSeek.
20
+
21
+ Permission is hereby granted, free of charge, to any person obtaining a copy
22
+ of this software and associated documentation files (the "Software"), to deal
23
+ in the Software without restriction, including without limitation the rights
24
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25
+ copies of the Software, and to permit persons to whom the Software is
26
+ furnished to do so, subject to the following conditions:
27
+
28
+ The above copyright notice and this permission notice shall be included in all
29
+ copies or substantial portions of the Software.
30
+
31
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # @hy-sde-org/dsh-internal-urls
2
+
3
+ FS-shaped **internal URL schemes** for DeepSeek Harness: `conflict://`,
4
+ `issue://`, and `pr://` resolved through one resolver registry
5
+ (`ctx.internalUrls`) that the read/grep/write tools consult before touching
6
+ the filesystem. Ported from the [@oh-my-pi](https://github.com/oh-my-pi)
7
+ coding-agent `internal-urls` system (see `port_omp.md` — the port lives in
8
+ the [hy-sde fork](https://github.com/hy-sde/deepseek-harness)) and shipped as
9
+ a **standalone plugin**: the registry row installs via `cordis.patch.yml`, and
10
+ the routing tools install as agent-scope shadows
11
+ (`@hy-sde-org/dsh-tool-fs-internal-urls` /
12
+ `@hy-sde-org/dsh-tool-fs-search-internal-urls`). Nothing in the upstream
13
+ DeepSeek Harness (`dsh-v0.1.1-rc.2` and later) needs to change.
14
+
15
+ ## URL shapes
16
+
17
+ - `conflict://<N>` — a recorded git conflict block (register one by reading
18
+ the conflicted file; `read` appends a resolution notice with the ids).
19
+ `conflict://<N>/ours|theirs|base` renders one side; `conflict://*` summarizes
20
+ every registered block. `write({ path: "conflict://<N>", content })` splices
21
+ the resolution into the backing file (`@ours` / `@theirs` / `@base` /
22
+ `@both` line tokens expand; `conflict://*` bulk-resolves). `<path>:conflicts`
23
+ reads a whole-file conflict summary.
24
+ - `issue://` / `pr://` — GitHub-as-filesystem through the `gh` CLI:
25
+ `issue://owner/repo`, `issue://123`, `issue://owner/repo/123`,
26
+ `pr://N/diff`, `pr://N/diff/all`, `pr://N/diff/<i>`, list options
27
+ (`?state=open&limit=30`, `?comments=0`).
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pnpm install --global @deepseek-ai/dsh
33
+ ```
34
+
35
+ ### Direct from npm (published)
36
+
37
+ All three packages are published on the npm registry under the `hy-sde-org`
38
+ organization (version `0.1.1-rc.2`). Add the service, then mount the routing
39
+ tools via a preset:
40
+
41
+ ```bash
42
+ dsh plugin --profile web add @hy-sde-org/dsh-internal-urls \
43
+ @hy-sde-org/dsh-tool-fs-internal-urls \
44
+ @hy-sde-org/dsh-tool-fs-search-internal-urls
45
+ ```
46
+
47
+ Then copy `examples/agent-preset/` from the installed package (or this repo)
48
+ to `~/.dsh/.agent-presets/<id>/` and select it in the Web UI preset picker.
49
+
50
+ ### From the git checkout (pre-publish / development)
51
+
52
+ ```bash
53
+ git clone git@github.com:hy-sde/dsh-internal-urls.git
54
+ cd dsh-internal-urls
55
+ pnpm install
56
+ pnpm run build
57
+
58
+ IU_TGZ="$(cd packages/internal-urls && pnpm pack --silent --pack-destination /tmp)"
59
+ FS_TGZ="$(cd packages/tool-fs-internal-urls && pnpm pack --silent --pack-destination /tmp)"
60
+ SEARCH_TGZ="$(cd packages/tool-fs-search-internal-urls && pnpm pack --silent --pack-destination /tmp)"
61
+ dsh plugin --profile web add "$IU_TGZ" "$FS_TGZ" "$SEARCH_TGZ"
62
+ ```
63
+
64
+ ## How stock-harness routing works
65
+
66
+ The official harness ships `dsh-tool-fs` (`read`/`write`/`edit`) and
67
+ `dsh-tool-fs-search` (`grep`) host-wide, with no seam into `ctx.internalUrls`
68
+ (that routing only exists in the hy-sde fork). This plugin therefore mounts
69
+ agent-scope **shadow** copies of those two tool suites that behave identically
70
+ on plain filesystem paths and route internal URLs through the registry when
71
+ mounted — harness scoped-tools semantics let the agent's own registration
72
+ shadow the global one, so only sessions using the preset preset get the
73
+ routing, and stock tool behavior elsewhere is untouched.
74
+
75
+ ## Service API
76
+
77
+ ```ts ignore-check
78
+ await ctx.plugin(InternalUrls)
79
+
80
+ const iu = ctx.internalUrls // InternalUrlsService
81
+ iu.register(handler) // one ProtocolHandler per scheme
82
+ await iu.resolve('conflict://1', { sessionKey, cwd, signal })
83
+ await iu.write('conflict://1', '@theirs', { sessionKey, cwd, signal, sandboxPolicy })
84
+ iu.conflicts(sessionKey) // per-session ConflictHistory
85
+ ```
86
+
87
+ Handlers are plain objects (`{ scheme, immutable, resolve, write?, complete? }`)
88
+ registered per scheme; the shipped `conflict://`, `issue://`, `pr://`
89
+ handlers mount with the plugin and are removed on unmount.
90
+
91
+ ## Config (the `internal-urls` row)
92
+
93
+ No configuration today — the row is inserted bare. Handler state (conflict
94
+ histories) is keyed by session inside the service; the optional
95
+ `SandboxExecutionPolicy` rides the write context opaque from the routing tool.
96
+
97
+ ## Tests
98
+
99
+ ```sh
100
+ pnpm -r --filter @hy-sde-org/dsh-internal-urls test
101
+ ```
@@ -0,0 +1,25 @@
1
+ # @hy-sde-org/dsh-internal-urls — install surface for FS-shaped internal URLs.
2
+ #
3
+ # The `ctx.internalUrls` SERVICE is host-plane: the resolver registry (and the
4
+ # per-session conflict histories it keys) crosses sessions, so this bundle
5
+ # inserts one row into the profile. The read/write/grep ROUTING that consumes
6
+ # it cannot live on the host plane on stock DeepSeek Harness: the official
7
+ # `dsh-tool-fs` always registers `read`/`write`/`edit` (and `dsh-tool-fs-search`
8
+ # registers `grep`), so a second owner anywhere in the host tree fails boot
9
+ # with "prompt section `tool:read` is already registered". The routing
10
+ # therefore ships in agent-scope shadow tools — `@hy-sde-org/dsh-tool-fs-internal-urls`
11
+ # and `@hy-sde-org/dsh-tool-fs-search-internal-urls` — mounted from a user
12
+ # agent preset (see examples/agent-preset/), where scoped shadowing makes
13
+ # them THE `read`/`write`/`grep` for that session while the deployment's
14
+ # stock tools keep serving every other scope untouched.
15
+ #
16
+ # `insert` is the stock harness patch form for rows that don't exist in the
17
+ # deployment's composition (a plain `id:`+`name:` entry is a modification of
18
+ # an existing row). This patch INSERTS a new row only — it touches no existing
19
+ # row, so `dsh plugin add @hy-sde-org/dsh-internal-urls` never breaks boot on
20
+ # a stock DeepSeek Harness (0.1.1-rc.2 and later). Without the preset rows the
21
+ # registry exists but no tool consults it.
22
+
23
+ - insert:
24
+ - id: internal-urls
25
+ name: '@hy-sde-org/dsh-internal-urls'
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Detecting, surfacing, and resolving git merge conflicts through FS-shaped
3
+ * URLs — the `conflict://` protocol. The read tool registers the conflict
4
+ * blocks it surfaces with a per-session history; the agent then resolves one
5
+ * with `write({ path: "conflict://<N>", content })`, or bulk-resolves every
6
+ * registered block with `write({ path: "conflict://*", content })`.
7
+ *
8
+ * Marker shape is strict: only column-0 markers of the exact prefix length
9
+ * followed by either EOL or a single space + label count. Lines that merely
10
+ * start with `<` or `=` never match.
11
+ *
12
+ * Ported from oh-my-pi (`packages/coding-agent/src/tools/conflict-detect.ts`),
13
+ * MIT, adapted to the DeepSeek Harness file-services seam.
14
+ * @module @hy-sde-org/dsh-internal-urls/conflict
15
+ */
16
+ import type { InternalResource, ParsedInternalUrl, ProtocolHandler, ResolveContext, WriteContext } from './types.ts';
17
+ /** One completed conflict block with its marker line numbers and side bodies. */
18
+ export interface ConflictBlock {
19
+ /** 1-indexed line of the `<<<<<<<` marker. */
20
+ startLine: number;
21
+ /** 1-indexed line of the `=======` separator. */
22
+ separatorLine: number;
23
+ /** 1-indexed line of the `>>>>>>>` marker. */
24
+ endLine: number;
25
+ /** 1-indexed line of the `|||||||` base marker (diff3 only). */
26
+ baseLine?: number;
27
+ oursLabel?: string;
28
+ baseLabel?: string;
29
+ theirsLabel?: string;
30
+ oursLines: string[];
31
+ baseLines?: string[];
32
+ theirsLines: string[];
33
+ }
34
+ /**
35
+ * Scan already-collected file lines for completed conflict blocks.
36
+ * `firstLineNumber` is the 1-indexed line number of `lines[0]` (so a windowed
37
+ * read starting at line 200 passes `firstLineNumber: 200`). Only fully-closed
38
+ * blocks are returned.
39
+ */
40
+ export declare function scanConflictLines(lines: readonly string[], firstLineNumber: number): ConflictBlock[];
41
+ /**
42
+ * Scan whole-file content (already decoded text) for conflict blocks. Returns
43
+ * only complete blocks; an unclosed opener at EOF yields none.
44
+ */
45
+ export declare function scanConflictsInContent(content: string): ConflictBlock[];
46
+ /** Recorded conflict block keyed by a session-stable id. */
47
+ export interface ConflictEntry extends ConflictBlock {
48
+ id: number;
49
+ absolutePath: string;
50
+ displayPath: string;
51
+ }
52
+ /**
53
+ * Per-session log of conflict regions surfaced by `read`/`:conflicts`. The
54
+ * history is append-only by content identity: re-reading a still-present block
55
+ * reuses its id, so retries never depend on re-reading even after other blocks
56
+ * in the file resolve.
57
+ */
58
+ export declare class ConflictHistory {
59
+ #private;
60
+ private nextId;
61
+ register(input: Omit<ConflictEntry, 'id'>): ConflictEntry;
62
+ get(id: number): ConflictEntry | undefined;
63
+ /** Every registered entry in id (insertion) order. */
64
+ entries(): ConflictEntry[];
65
+ /** Drop a single entry by id; used after a successful resolve. */
66
+ invalidate(id: number): void;
67
+ /** Drop every entry referencing `absolutePath`. */
68
+ invalidatePath(absolutePath: string): void;
69
+ }
70
+ /** A side of a conflict block addressable as `conflict://N/<scope>`. */
71
+ export type ConflictScope = 'ours' | 'theirs' | 'base';
72
+ /** Parsed `conflict://<N>` / `conflict://<N>/<scope>` / `conflict://*` URI. */
73
+ export interface ParsedConflictUri {
74
+ /** `"*"` selects every currently-registered conflict (bulk write only). */
75
+ id: number | '*';
76
+ scope?: ConflictScope;
77
+ }
78
+ /**
79
+ * Parse a `conflict://<N>`, `conflict://<N>/<scope>`, or `conflict://*` URI.
80
+ * Returns `null` for non-conflict paths; throws for a well-formed scheme with
81
+ * an invalid id or scope so the agent gets a clear message.
82
+ */
83
+ export declare function parseConflictUri(raw: string): ParsedConflictUri | null;
84
+ /** A side of a conflict block that read can render via `conflict://N/<scope>`. */
85
+ export declare function renderConflictRegion(entry: ConflictEntry, scope: ConflictScope | undefined): {
86
+ lines: string[];
87
+ startLine: number;
88
+ };
89
+ /**
90
+ * Expand `@ours` / `@theirs` / `@base` / `@both` line tokens against the
91
+ * recorded sections of `entry`. A token only triggers when it is the entire
92
+ * content of a line; other lines pass through verbatim. `@base` throws when
93
+ * the conflict was 2-way (no base recorded).
94
+ */
95
+ export declare function expandContentTokens(content: string, entry: ConflictEntry): string;
96
+ /**
97
+ * Splice the conflict region recorded in `entry` out of `originalText` and
98
+ * replace it with `replacement`, markers and all sides included. Locates the
99
+ * recorded marker block by content anchored to the recorded start line, so
100
+ * out-of-band edits earlier in the file that shift line numbers don't break
101
+ * resolution. Replacement lines that exactly echo the adjacent context are
102
+ * dropped (the universal "whole function" paste artifact). Returns the new
103
+ * text and how many echo lines were trimmed at each boundary.
104
+ */
105
+ export declare function spliceConflict(originalText: string, entry: ConflictEntry, replacement: string): {
106
+ text: string;
107
+ trimmedLeading: number;
108
+ trimmedTrailing: number;
109
+ };
110
+ /**
111
+ * Build a compact diff-style footer describing the conflicts registered during
112
+ * a read. Appended to the read output so the agent learns the `conflict://<N>`
113
+ * ids and the resolution protocol.
114
+ */
115
+ export declare function formatConflictWarning(entries: readonly ConflictEntry[]): string;
116
+ /**
117
+ * Render a one-line-per-block index of every registered conflict. Used by the
118
+ * `<path>:conflicts` read selector to give the agent a cheap overview of a
119
+ * heavily-conflicted file without dumping every body.
120
+ */
121
+ export declare function formatConflictSummary(entries: readonly ConflictEntry[], displayPath: string): string;
122
+ /**
123
+ * File access the conflict handler performs resolution through — provided by
124
+ * the service plugin against `ctx.fs` so splice/rewrite keep every seam
125
+ * guarantee (target identity, atomic publish, sandbox policy).
126
+ */
127
+ export interface ConflictFileBridge {
128
+ /** Read the whole current file text at `absolutePath`. */
129
+ readFile(absolutePath: string, signal?: AbortSignal): Promise<string>;
130
+ /** Replace `absolutePath` with `content` (unconditional atomic write). */
131
+ writeFile(absolutePath: string, content: string, signal?: AbortSignal, sandboxPolicy?: unknown): Promise<void>;
132
+ }
133
+ /**
134
+ * The `conflict://` protocol handler.
135
+ *
136
+ * Reads:
137
+ * - `conflict://<N>` — full recorded marker block, lines prefixed by file line numbers.
138
+ * - `conflict://<N>/ours|theirs|base` — one side only.
139
+ * - `conflict://*` — summary of every currently-registered block.
140
+ *
141
+ * Writes:
142
+ * - `conflict://<N>` — splice the recorded region, replace with `content`
143
+ * (`@ours`/`@theirs`/`@base`/`@both` line tokens expand).
144
+ * - `conflict://*` — bulk: either one `content` for every registered block,
145
+ * or per-id lines `"N: @side"` resolving each listed id.
146
+ */
147
+ export declare class ConflictProtocolHandler implements ProtocolHandler {
148
+ private readonly deps;
149
+ readonly scheme = "conflict";
150
+ readonly immutable = false;
151
+ constructor(deps: {
152
+ historyFor: (sessionKey: string | undefined) => ConflictHistory;
153
+ bridge: ConflictFileBridge;
154
+ });
155
+ resolve(url: ParsedInternalUrl, context?: ResolveContext): Promise<InternalResource>;
156
+ write(url: ParsedInternalUrl, content: string, context?: WriteContext): Promise<void>;
157
+ /** Apply the same (token-expanded) content to every registered block. */
158
+ private writeBulk;
159
+ private resolveEntry;
160
+ }
161
+ //# sourceMappingURL=conflict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflict.d.ts","sourceRoot":"","sources":["../src/conflict.ts"],"names":[],"mappings":"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"}