dsh-live-teams 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.
- package/LICENSE +176 -0
- package/NOTICE +11 -0
- package/README.md +85 -0
- package/cordis.patch.yml +25 -0
- package/lib/binding.d.ts +18 -0
- package/lib/binding.js +42 -0
- package/lib/changed-paths.d.ts +26 -0
- package/lib/changed-paths.js +69 -0
- package/lib/client.js +6753 -0
- package/lib/command-queue.d.ts +60 -0
- package/lib/command-queue.js +185 -0
- package/lib/compatibility.js +109 -0
- package/lib/context-provider.d.ts +110 -0
- package/lib/context-provider.js +249 -0
- package/lib/dispatch.d.ts +174 -0
- package/lib/dispatch.js +624 -0
- package/lib/errors.d.ts +36 -0
- package/lib/errors.js +103 -0
- package/lib/git-artifacts.d.ts +50 -0
- package/lib/git-artifacts.js +242 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.js +14 -0
- package/lib/mailbox.d.ts +274 -0
- package/lib/mailbox.js +721 -0
- package/lib/member-tools.d.ts +57 -0
- package/lib/member-tools.js +1265 -0
- package/lib/migrations.d.ts +17 -0
- package/lib/migrations.js +47 -0
- package/lib/plugin.d.ts +106 -0
- package/lib/plugin.js +1003 -0
- package/lib/roles.d.ts +35 -0
- package/lib/roles.js +284 -0
- package/lib/routes.d.ts +586 -0
- package/lib/routes.js +2816 -0
- package/lib/scope.d.ts +62 -0
- package/lib/scope.js +133 -0
- package/lib/session-bridge.d.ts +76 -0
- package/lib/session-bridge.js +147 -0
- package/lib/session-title.js +35 -0
- package/lib/storage.d.ts +9 -0
- package/lib/storage.js +65 -0
- package/lib/task-store.d.ts +729 -0
- package/lib/task-store.js +2205 -0
- package/lib/team-store.d.ts +216 -0
- package/lib/team-store.js +765 -0
- package/lib/tree-snapshot.d.ts +28 -0
- package/lib/tree-snapshot.js +80 -0
- package/lib/types/client/TeamView.d.ts +26 -0
- package/lib/types/client/TeamView.dom.test.d.ts +1 -0
- package/lib/types/client/api.d.ts +522 -0
- package/lib/types/client/api.test.d.ts +1 -0
- package/lib/types/client/attention.d.ts +65 -0
- package/lib/types/client/attention.test.d.ts +1 -0
- package/lib/types/client/index.d.ts +31 -0
- package/lib/types/client/locales.d.ts +577 -0
- package/lib/types/client/member-name.d.ts +14 -0
- package/lib/types/client/member-name.test.d.ts +1 -0
- package/lib/types/client/roster.d.ts +26 -0
- package/lib/types/client/roster.test.d.ts +1 -0
- package/lib/types/client/styles.d.ts +3 -0
- package/package.json +104 -0
- package/roles/builder.md +40 -0
- package/roles/delegate.md +36 -0
- package/roles/lead.md +46 -0
- package/roles/oracle.md +36 -0
- package/roles/researcher.md +37 -0
- package/roles/reviewer.md +45 -0
- package/roles/scout.md +36 -0
- package/roles/verifier.md +36 -0
package/lib/errors.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
//#region src/errors.ts
|
|
2
|
+
/** Stable host-side error taxonomy and platform translation. */
|
|
3
|
+
const ERROR_CODES = Object.freeze({
|
|
4
|
+
SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
|
|
5
|
+
SESSION_WRONG_WORKSPACE: "SESSION_WRONG_WORKSPACE",
|
|
6
|
+
SESSION_BUSY: "SESSION_BUSY",
|
|
7
|
+
DELIVERY_REJECTED: "DELIVERY_REJECTED",
|
|
8
|
+
MODEL_UNAVAILABLE: "MODEL_UNAVAILABLE",
|
|
9
|
+
INTERRUPT_UNSUPPORTED: "INTERRUPT_UNSUPPORTED",
|
|
10
|
+
PLUGIN_DISPOSING: "PLUGIN_DISPOSING",
|
|
11
|
+
CAPABILITY_UNAVAILABLE: "CAPABILITY_UNAVAILABLE",
|
|
12
|
+
REVISION_CONFLICT: "REVISION_CONFLICT",
|
|
13
|
+
BINDING_STALE: "BINDING_STALE",
|
|
14
|
+
SCHEMA_UNSUPPORTED: "SCHEMA_UNSUPPORTED",
|
|
15
|
+
STATE_CORRUPT: "STATE_CORRUPT",
|
|
16
|
+
/** ADR 0006: the reviewed base moved before integration, so the decision must be refreshed. */
|
|
17
|
+
STALE_BASE: "STALE_BASE"
|
|
18
|
+
});
|
|
19
|
+
/** Audit text used when a member was rebound after command admission. */
|
|
20
|
+
const BINDING_STALE_MESSAGE = "the member was rebound after this command was admitted";
|
|
21
|
+
const SUBAGENT_OWNERSHIP_REASON = "use subagent delivery for this child session";
|
|
22
|
+
const platformErrorMap = /* @__PURE__ */ new Map([
|
|
23
|
+
["session/not-found", "SESSION_NOT_FOUND"],
|
|
24
|
+
["session/conflict", "SESSION_WRONG_WORKSPACE"],
|
|
25
|
+
["session/workspace-attach-failed", "SESSION_WRONG_WORKSPACE"],
|
|
26
|
+
["workspace/not-found", "SESSION_WRONG_WORKSPACE"],
|
|
27
|
+
["session/steer-unavailable", "DELIVERY_REJECTED"],
|
|
28
|
+
["session/attachment-invalid", "DELIVERY_REJECTED"],
|
|
29
|
+
["session/invalid-time-zone", "DELIVERY_REJECTED"],
|
|
30
|
+
["session/queue-item-not-found", "DELIVERY_REJECTED"],
|
|
31
|
+
["session/model-unavailable", "MODEL_UNAVAILABLE"],
|
|
32
|
+
["llm/model-discovery-rejected", "MODEL_UNAVAILABLE"],
|
|
33
|
+
["session/title-invalid", "CAPABILITY_UNAVAILABLE"],
|
|
34
|
+
["agent-preset/conflict", "CAPABILITY_UNAVAILABLE"],
|
|
35
|
+
["session/fork-unavailable", "CAPABILITY_UNAVAILABLE"],
|
|
36
|
+
["settings/rejected", "CAPABILITY_UNAVAILABLE"],
|
|
37
|
+
["credential/rejected", "CAPABILITY_UNAVAILABLE"],
|
|
38
|
+
["subagent/not-found", "CAPABILITY_UNAVAILABLE"],
|
|
39
|
+
["subagent/unauthorized", "CAPABILITY_UNAVAILABLE"],
|
|
40
|
+
["subagent/catalog-diagnostic", "CAPABILITY_UNAVAILABLE"],
|
|
41
|
+
["gateway/cancelled", "PLUGIN_DISPOSING"],
|
|
42
|
+
["gateway/bad-request", "CAPABILITY_UNAVAILABLE"],
|
|
43
|
+
["gateway/internal", "CAPABILITY_UNAVAILABLE"]
|
|
44
|
+
]);
|
|
45
|
+
function isErrorCode(code) {
|
|
46
|
+
return Object.prototype.hasOwnProperty.call(ERROR_CODES, code);
|
|
47
|
+
}
|
|
48
|
+
function rawMessage(value) {
|
|
49
|
+
if (value instanceof Error) return value.message;
|
|
50
|
+
if (typeof value === "string") return value;
|
|
51
|
+
if (value !== null && typeof value === "object" && "message" in value) {
|
|
52
|
+
const message = value.message;
|
|
53
|
+
if (typeof message === "string") return message;
|
|
54
|
+
}
|
|
55
|
+
return String(value ?? "Unknown platform error");
|
|
56
|
+
}
|
|
57
|
+
function platformCodeOf(value) {
|
|
58
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
59
|
+
const object = value;
|
|
60
|
+
if (typeof object.code === "string") return object.code;
|
|
61
|
+
if (typeof object.details?.code === "string") return object.details.code;
|
|
62
|
+
}
|
|
63
|
+
function platformReasonOf(value) {
|
|
64
|
+
if (value === null || typeof value !== "object") return void 0;
|
|
65
|
+
const details = value.details;
|
|
66
|
+
return typeof details?.reason === "string" ? details.reason : void 0;
|
|
67
|
+
}
|
|
68
|
+
var LiveTeamsError = class extends Error {
|
|
69
|
+
code;
|
|
70
|
+
auditDetail;
|
|
71
|
+
details;
|
|
72
|
+
constructor(code, message, options = {}) {
|
|
73
|
+
const normalizedCode = isErrorCode(code) ? code : "CAPABILITY_UNAVAILABLE";
|
|
74
|
+
super(message, options.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
75
|
+
this.name = "LiveTeamsError";
|
|
76
|
+
this.code = normalizedCode;
|
|
77
|
+
this.auditDetail = options.auditDetail ?? message;
|
|
78
|
+
this.details = options.details ?? {};
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
/** Translate platform failures into stable domain errors. */
|
|
82
|
+
function translatePlatformError(error) {
|
|
83
|
+
if (error instanceof LiveTeamsError) return error;
|
|
84
|
+
const sourceCode = platformCodeOf(error);
|
|
85
|
+
const reason = platformReasonOf(error);
|
|
86
|
+
let code;
|
|
87
|
+
if (sourceCode === "session/agent-busy") code = reason?.includes(SUBAGENT_OWNERSHIP_REASON) ? "CAPABILITY_UNAVAILABLE" : "SESSION_BUSY";
|
|
88
|
+
else code = (sourceCode === void 0 ? void 0 : platformErrorMap.get(sourceCode)) ?? "CAPABILITY_UNAVAILABLE";
|
|
89
|
+
const detail = sourceCode === void 0 || platformErrorMap.has(sourceCode) || sourceCode === "session/agent-busy" ? rawMessage(error) : `platform code ${sourceCode}: ${rawMessage(error)}`;
|
|
90
|
+
return new LiveTeamsError(code, code, {
|
|
91
|
+
auditDetail: detail,
|
|
92
|
+
cause: error,
|
|
93
|
+
details: {
|
|
94
|
+
...sourceCode === void 0 ? {} : { platformCode: sourceCode },
|
|
95
|
+
...reason === void 0 ? {} : { reason }
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function platformErrorMappings() {
|
|
100
|
+
return platformErrorMap;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { BINDING_STALE_MESSAGE, ERROR_CODES, LiveTeamsError, platformErrorMappings, translatePlatformError };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/git-artifacts.d.ts
|
|
2
|
+
export type BaseInfo = {
|
|
3
|
+
readonly ref: string;
|
|
4
|
+
readonly sha: string;
|
|
5
|
+
};
|
|
6
|
+
/** The branch and tip a piece of work is based on. `undefined` in a repository with no commits. */
|
|
7
|
+
export declare function baseInfo(workspacePath: string): Promise<BaseInfo | undefined>;
|
|
8
|
+
export type BuiltCommit = {
|
|
9
|
+
readonly headSha: string;
|
|
10
|
+
readonly treeSha: string;
|
|
11
|
+
readonly commitPaths: readonly string[];
|
|
12
|
+
readonly mergeBaseSha?: string;
|
|
13
|
+
};
|
|
14
|
+
export type CommitRequest = {
|
|
15
|
+
readonly paths: readonly string[];
|
|
16
|
+
readonly message: string;
|
|
17
|
+
readonly author: {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly email: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Build one commit from exactly these paths, parented on `base`.
|
|
24
|
+
*
|
|
25
|
+
* The tree is the *base tree* with the submitted paths replaced: a commit built from scratch would
|
|
26
|
+
* delete everything the submission did not mention, which the first probe of this module caught by
|
|
27
|
+
* showing four changed paths where two were submitted. Blobs are written with `hash-object -w`,
|
|
28
|
+
* directories assembled bottom-up with `mktree`, and `commit-tree` writes the commit — no index, no
|
|
29
|
+
* checkout, no branch moved.
|
|
30
|
+
*
|
|
31
|
+
* A submitted path that is missing from disk is a deletion when the base had it, and a refusal when
|
|
32
|
+
* it did not: a submission cannot claim a file that never existed.
|
|
33
|
+
*/
|
|
34
|
+
export declare function commitPaths(workspacePath: string, base: BaseInfo | undefined, request: CommitRequest): Promise<BuiltCommit | undefined>;
|
|
35
|
+
/** Keep a built commit reachable, so `git gc` cannot take the reviewed artifact away. */
|
|
36
|
+
export declare function pinCommit(workspacePath: string, ref: string, sha: string): Promise<boolean>;
|
|
37
|
+
/** The current tip of a branch, for the acceptance precondition. */
|
|
38
|
+
export declare function refTip(workspacePath: string, ref: string): Promise<string | undefined>;
|
|
39
|
+
/**
|
|
40
|
+
* Integrate by compare-and-swap: advance the branch only if it is still where the review left it.
|
|
41
|
+
*
|
|
42
|
+
* The working tree already holds the work, so nothing is checked out and nothing is merged. The
|
|
43
|
+
* old-value argument is what makes this safe: a base that moved underneath the decision fails here
|
|
44
|
+
* instead of silently rewriting history or absorbing a review that no longer applies.
|
|
45
|
+
*/
|
|
46
|
+
export type IntegrationOutcome = 'integrated' | 'stale' | 'index-failed';
|
|
47
|
+
export declare function integrateCommit(workspacePath: string, ref: string, headSha: string, expectedTip: string | undefined, commitPaths?: readonly string[]): Promise<IntegrationOutcome>;
|
|
48
|
+
/** Paths a commit changes relative to a base, for a reviewer that wants the list. */
|
|
49
|
+
export declare function commitPathsOf(workspacePath: string, from: string, to: string): Promise<string[] | undefined>;
|
|
50
|
+
//#endregion
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import * as path$1 from "node:path";
|
|
2
|
+
import * as fsp from "node:fs/promises";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
4
|
+
//#region src/git-artifacts.ts
|
|
5
|
+
/**
|
|
6
|
+
* Git plumbing for Git-bound submissions (Phase 6, ADR 0006).
|
|
7
|
+
*
|
|
8
|
+
* The plugin — not the member — turns the submitted paths into one commit, and it does so without
|
|
9
|
+
* touching the branch, the index or any other file in the tree. That matters because members share
|
|
10
|
+
* one checkout: switching branches or staging paths would disturb a colleague's work in progress.
|
|
11
|
+
* Everything here is plumbing, so the only visible effect is a new object and a plugin-owned ref.
|
|
12
|
+
*
|
|
13
|
+
* `undefined` means "not observable" everywhere: no repository, no commit, an unreadable answer.
|
|
14
|
+
* The caller records that honestly rather than inventing a result.
|
|
15
|
+
*/
|
|
16
|
+
const GIT_TIMEOUT_MS = 2e4;
|
|
17
|
+
async function git(workspacePath, args, options = {}) {
|
|
18
|
+
return await new Promise((resolve) => {
|
|
19
|
+
const child = execFile("git", [
|
|
20
|
+
"-C",
|
|
21
|
+
workspacePath,
|
|
22
|
+
...args
|
|
23
|
+
], {
|
|
24
|
+
timeout: GIT_TIMEOUT_MS,
|
|
25
|
+
maxBuffer: 8388608,
|
|
26
|
+
env: options.env === void 0 ? process.env : {
|
|
27
|
+
...process.env,
|
|
28
|
+
...options.env
|
|
29
|
+
}
|
|
30
|
+
}, (error, stdout) => resolve({
|
|
31
|
+
stdout: String(stdout),
|
|
32
|
+
ok: error === null
|
|
33
|
+
}));
|
|
34
|
+
if (options.input !== void 0) child.stdin?.end(options.input);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/** The branch and tip a piece of work is based on. `undefined` in a repository with no commits. */
|
|
38
|
+
async function baseInfo(workspacePath) {
|
|
39
|
+
const head = await git(workspacePath, ["rev-parse", "HEAD"]);
|
|
40
|
+
if (!head.ok) return void 0;
|
|
41
|
+
const sha = head.stdout.trim();
|
|
42
|
+
const symbolic = await git(workspacePath, [
|
|
43
|
+
"symbolic-ref",
|
|
44
|
+
"-q",
|
|
45
|
+
"HEAD"
|
|
46
|
+
]);
|
|
47
|
+
if (!symbolic.ok) return void 0;
|
|
48
|
+
const ref = symbolic.stdout.trim();
|
|
49
|
+
return ref.length === 0 || !/^[0-9a-f]{40}$/u.test(sha) ? void 0 : {
|
|
50
|
+
ref,
|
|
51
|
+
sha
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** The entries of a commit's tree, flattened to `path → mode:sha`, for building on top of it. */
|
|
55
|
+
async function baseTree(workspacePath, base) {
|
|
56
|
+
const entries = /* @__PURE__ */ new Map();
|
|
57
|
+
if (base === void 0) return entries;
|
|
58
|
+
const listed = await git(workspacePath, [
|
|
59
|
+
"ls-tree",
|
|
60
|
+
"-r",
|
|
61
|
+
"-z",
|
|
62
|
+
base.sha
|
|
63
|
+
]);
|
|
64
|
+
if (!listed.ok) return void 0;
|
|
65
|
+
for (const record of listed.stdout.split("\0")) {
|
|
66
|
+
if (record.length === 0) continue;
|
|
67
|
+
const match = /^(\d{6}) (\w+) ([0-9a-f]{40})\t(.+)$/su.exec(record);
|
|
68
|
+
if (match === null || match[2] !== "blob") continue;
|
|
69
|
+
entries.set(match[4], {
|
|
70
|
+
mode: match[1],
|
|
71
|
+
sha: match[3]
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return entries;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build one commit from exactly these paths, parented on `base`.
|
|
78
|
+
*
|
|
79
|
+
* The tree is the *base tree* with the submitted paths replaced: a commit built from scratch would
|
|
80
|
+
* delete everything the submission did not mention, which the first probe of this module caught by
|
|
81
|
+
* showing four changed paths where two were submitted. Blobs are written with `hash-object -w`,
|
|
82
|
+
* directories assembled bottom-up with `mktree`, and `commit-tree` writes the commit — no index, no
|
|
83
|
+
* checkout, no branch moved.
|
|
84
|
+
*
|
|
85
|
+
* A submitted path that is missing from disk is a deletion when the base had it, and a refusal when
|
|
86
|
+
* it did not: a submission cannot claim a file that never existed.
|
|
87
|
+
*/
|
|
88
|
+
async function commitPaths(workspacePath, base, request) {
|
|
89
|
+
if (request.paths.length === 0) return void 0;
|
|
90
|
+
const sorted = [...new Set(request.paths)].sort((left, right) => left.localeCompare(right));
|
|
91
|
+
const entries = await baseTree(workspacePath, base);
|
|
92
|
+
if (entries === void 0) return void 0;
|
|
93
|
+
for (const relative of sorted) {
|
|
94
|
+
const absolute = path$1.resolve(workspacePath, relative);
|
|
95
|
+
if (absolute !== workspacePath && !absolute.startsWith(`${workspacePath}${path$1.sep}`)) return void 0;
|
|
96
|
+
let content;
|
|
97
|
+
let mode = "100644";
|
|
98
|
+
try {
|
|
99
|
+
const stat = await fsp.lstat(absolute);
|
|
100
|
+
if (stat.isSymbolicLink()) {
|
|
101
|
+
content = Buffer.from(await fsp.readlink(absolute));
|
|
102
|
+
mode = "120000";
|
|
103
|
+
} else {
|
|
104
|
+
content = await fsp.readFile(absolute);
|
|
105
|
+
if ((stat.mode & 73) !== 0) mode = "100755";
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
content = void 0;
|
|
109
|
+
}
|
|
110
|
+
if (content === void 0) {
|
|
111
|
+
if (!entries.has(relative)) return void 0;
|
|
112
|
+
entries.delete(relative);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const written = await git(workspacePath, [
|
|
116
|
+
"hash-object",
|
|
117
|
+
"-w",
|
|
118
|
+
"--stdin"
|
|
119
|
+
], { input: content });
|
|
120
|
+
if (!written.ok) return void 0;
|
|
121
|
+
entries.set(relative, {
|
|
122
|
+
mode,
|
|
123
|
+
sha: written.stdout.trim()
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const treeOf = async (prefix) => {
|
|
127
|
+
const fileLines = [];
|
|
128
|
+
const parentOf = (directory) => {
|
|
129
|
+
const trimmed = directory.endsWith("/") ? directory.slice(0, -1) : directory;
|
|
130
|
+
const cut = trimmed.lastIndexOf("/");
|
|
131
|
+
return cut < 0 ? "" : trimmed.slice(0, cut + 1);
|
|
132
|
+
};
|
|
133
|
+
const directories = /* @__PURE__ */ new Set();
|
|
134
|
+
for (const [relative, entry] of entries) {
|
|
135
|
+
if (!relative.startsWith(prefix)) continue;
|
|
136
|
+
const rest = relative.slice(prefix.length);
|
|
137
|
+
const slash = rest.indexOf("/");
|
|
138
|
+
if (slash >= 0) {
|
|
139
|
+
directories.add(`${prefix}${rest.slice(0, slash)}/`);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
fileLines.push(`${entry.mode} blob ${entry.sha}\t${rest}`);
|
|
143
|
+
}
|
|
144
|
+
const treeLines = [];
|
|
145
|
+
for (const directory of [...directories].sort()) {
|
|
146
|
+
if (parentOf(directory) !== prefix) continue;
|
|
147
|
+
const child = await treeOf(directory);
|
|
148
|
+
if (child === void 0) return void 0;
|
|
149
|
+
treeLines.push(`040000 tree ${child}\t${directory.slice(prefix.length, -1)}`);
|
|
150
|
+
}
|
|
151
|
+
const built = await git(workspacePath, ["mktree"], { input: `${[...treeLines, ...fileLines].join("\n")}\n` });
|
|
152
|
+
if (!built.ok) return void 0;
|
|
153
|
+
return built.stdout.trim();
|
|
154
|
+
};
|
|
155
|
+
const treeSha = await treeOf("");
|
|
156
|
+
if (treeSha === void 0) return void 0;
|
|
157
|
+
const args = ["commit-tree", treeSha];
|
|
158
|
+
if (base !== void 0) args.push("-p", base.sha);
|
|
159
|
+
args.push("-m", request.message);
|
|
160
|
+
const committed = await git(workspacePath, args, { env: {
|
|
161
|
+
GIT_AUTHOR_NAME: request.author.name,
|
|
162
|
+
GIT_AUTHOR_EMAIL: request.author.email,
|
|
163
|
+
GIT_COMMITTER_NAME: "live-teams",
|
|
164
|
+
GIT_COMMITTER_EMAIL: "live-teams@localhost"
|
|
165
|
+
} });
|
|
166
|
+
if (!committed.ok) return void 0;
|
|
167
|
+
const headSha = committed.stdout.trim();
|
|
168
|
+
if (!/^[0-9a-f]{40}$/u.test(headSha)) return void 0;
|
|
169
|
+
let mergeBaseSha;
|
|
170
|
+
if (base !== void 0) {
|
|
171
|
+
const baseMerge = await git(workspacePath, [
|
|
172
|
+
"merge-base",
|
|
173
|
+
base.sha,
|
|
174
|
+
headSha
|
|
175
|
+
]);
|
|
176
|
+
if (baseMerge.ok) mergeBaseSha = baseMerge.stdout.trim() || void 0;
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
headSha,
|
|
180
|
+
treeSha,
|
|
181
|
+
commitPaths: sorted,
|
|
182
|
+
...mergeBaseSha === void 0 ? {} : { mergeBaseSha }
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/** Keep a built commit reachable, so `git gc` cannot take the reviewed artifact away. */
|
|
186
|
+
async function pinCommit(workspacePath, ref, sha) {
|
|
187
|
+
return (await git(workspacePath, [
|
|
188
|
+
"update-ref",
|
|
189
|
+
ref,
|
|
190
|
+
sha
|
|
191
|
+
])).ok;
|
|
192
|
+
}
|
|
193
|
+
/** The current tip of a branch, for the acceptance precondition. */
|
|
194
|
+
async function refTip(workspacePath, ref) {
|
|
195
|
+
const tip = await git(workspacePath, [
|
|
196
|
+
"rev-parse",
|
|
197
|
+
"--verify",
|
|
198
|
+
"--quiet",
|
|
199
|
+
ref
|
|
200
|
+
]);
|
|
201
|
+
return tip.ok && /^[0-9a-f]{40}$/u.test(tip.stdout.trim()) ? tip.stdout.trim() : void 0;
|
|
202
|
+
}
|
|
203
|
+
async function repairIndex(workspacePath, commitPaths) {
|
|
204
|
+
if (commitPaths.length === 0) return true;
|
|
205
|
+
return (await git(workspacePath, [
|
|
206
|
+
"reset",
|
|
207
|
+
"-q",
|
|
208
|
+
"--",
|
|
209
|
+
...commitPaths
|
|
210
|
+
])).ok;
|
|
211
|
+
}
|
|
212
|
+
async function integrateCommit(workspacePath, ref, headSha, expectedTip, commitPaths = []) {
|
|
213
|
+
const current = await git(workspacePath, [
|
|
214
|
+
"rev-parse",
|
|
215
|
+
"--verify",
|
|
216
|
+
"--quiet",
|
|
217
|
+
ref
|
|
218
|
+
]);
|
|
219
|
+
const tip = current.ok && /^[0-9a-f]{40}$/u.test(current.stdout.trim()) ? current.stdout.trim() : void 0;
|
|
220
|
+
if (tip === headSha) return await repairIndex(workspacePath, commitPaths) ? "integrated" : "index-failed";
|
|
221
|
+
if (expectedTip !== void 0 && tip !== expectedTip) return "stale";
|
|
222
|
+
const args = [
|
|
223
|
+
"update-ref",
|
|
224
|
+
ref,
|
|
225
|
+
headSha
|
|
226
|
+
];
|
|
227
|
+
if (expectedTip !== void 0) args.push(expectedTip);
|
|
228
|
+
if (!(await git(workspacePath, args)).ok) return "stale";
|
|
229
|
+
return await repairIndex(workspacePath, commitPaths) ? "integrated" : "index-failed";
|
|
230
|
+
}
|
|
231
|
+
/** Paths a commit changes relative to a base, for a reviewer that wants the list. */
|
|
232
|
+
async function commitPathsOf(workspacePath, from, to) {
|
|
233
|
+
const diff = await git(workspacePath, [
|
|
234
|
+
"diff",
|
|
235
|
+
"--name-only",
|
|
236
|
+
`${from}..${to}`
|
|
237
|
+
]);
|
|
238
|
+
if (!diff.ok) return void 0;
|
|
239
|
+
return diff.stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
export { baseInfo, commitPaths, commitPathsOf, integrateCommit, pinCommit, refTip };
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AcknowledgeResult, CommandQueueLike, CreateMailboxOptions, MailboxActor, MailboxAuditEvent, MailboxAuthorizeRequest, MailboxOptions, MailboxRecipient, MailboxService, MessageAckPolicy, MessageDelivery, MessageDeliveryOutcome, MessageDeliveryState, MessageDraft, MessageKind, MessageWakePolicy, ResolvedMailboxRecipient, SendResult, TeamMessage, createMailbox, messageDeliveryOutcome, messagePolicy, resolveMailboxRecipient } from "./mailbox.js";
|
|
2
|
+
import { BINDING_STALE_MESSAGE, ERROR_CODES, ErrorCode, ErrorOptions, LiveTeamsError, platformErrorMappings, translatePlatformError } from "./errors.js";
|
|
3
|
+
import { BindingGeneration, ContactGrant, ContactTarget, MemberId, MemberRecord, SessionId, TEAM_ROOT, TeamAdoption, TeamCandidate, TeamId, TeamState, TeamStore, createTeamStore, readMemberBySessionSync, readTeamStateSync } from "./team-store.js";
|
|
4
|
+
import { AcceptTaskRequest, AcceptTaskResult, AcknowledgeTaskRequest, AttemptsState, ClaimTaskRequest, ClaimTaskResult, CreateTaskRequest, CreateTaskResult, HumanContact, ListTasksOptions, ReadTaskResult, ReviewFinding, ReviewRound, ReviewTaskRequest, ReviewTaskResult, ReviewVerdict, ReviewsState, ReviseTaskRequest, ReviseTaskResult, Submission, SubmissionArtifact, SubmissionCheck, SubmissionSourceArtifact, SubmissionsState, SubmitTaskRequest, TaskAttempt, TaskKind, TaskRoute, TaskStatus, TaskStore, TasksState, TeamTask, assignmentRouteText, createTaskStore, deriveRouteContacts, renderDocument, routeAllowsContact, routeText, taskDocumentPath } from "./task-store.js";
|
|
5
|
+
import { SessionBridge, SessionBridgeDeps, SessionController, SessionDescriptor, createSessionBridge, toBridgeError } from "./session-bridge.js";
|
|
6
|
+
import { CommandRecord, CommandState, SessionCommandQueue, createCommandQueue } from "./command-queue.js";
|
|
7
|
+
import { LiveTeamsRole, ParsedRole, RolePermissions, RoleRegistry, RoleRegistrySync, parseRoleDocument, readRoleRegistry, readRoleRegistrySync } from "./roles.js";
|
|
8
|
+
import { MEMBERSHIP_CONTEXT_NAME, MEMBERSHIP_CONTEXT_ORDER, MembershipContextBinding, MembershipContextOptions, MembershipContextProvider, MembershipMember, MembershipScope, createMembershipContextProvider, renderMembership, sessionIdOf } from "./context-provider.js";
|
|
9
|
+
import { JournalCategory, JournalEntry, JournalRefs, LIVE_TEAMS_CANDIDATES_ROUTE, LIVE_TEAMS_JOURNAL_ROUTE, LIVE_TEAMS_MEMBERS_ROUTE, LIVE_TEAMS_MEMBER_AVAILABILITY_ROUTE, LIVE_TEAMS_MEMBER_CREATE_SESSION_ROUTE, LIVE_TEAMS_MEMBER_INTERRUPT_ROUTE, LIVE_TEAMS_MEMBER_NAME_ROUTE, LIVE_TEAMS_MEMBER_REMOVE_ROUTE, LIVE_TEAMS_MEMBER_ROLE_ROUTE, LIVE_TEAMS_MESSAGES_ACKNOWLEDGE_ROUTE, LIVE_TEAMS_MESSAGES_ROUTE, LIVE_TEAMS_ROLES_OPEN_ROUTE, LIVE_TEAMS_ROLES_OVERRIDE_ROUTE, LIVE_TEAMS_ROLES_ROUTE, LIVE_TEAMS_TASKS_ROUTE, LIVE_TEAMS_TASK_ACCEPT_ROUTE, LIVE_TEAMS_TASK_REVISE_ROUTE, LIVE_TEAMS_TASK_ROUTE, LIVE_TEAMS_TEAM_ADOPT_ROUTE, LIVE_TEAMS_TEAM_BRIEFING_OPEN_ROUTE, LIVE_TEAMS_TEAM_CANDIDATES_ROUTE, LIVE_TEAMS_TEAM_DISABLE_ROUTE, LIVE_TEAMS_TEAM_ENABLE_ROUTE, LIVE_TEAMS_TEAM_LEAD_ROUTE, LIVE_TEAMS_TEAM_NAME_ROUTE, LIVE_TEAMS_TEAM_ROUTE, LiveTeamsAdoptionCandidate, LiveTeamsAuditRecord, LiveTeamsCandidate, LiveTeamsCandidates, LiveTeamsCreateMemberRequest, LiveTeamsCreateMemberResult, LiveTeamsCreateSessionMemberRequest, LiveTeamsCreateSessionMemberResult, LiveTeamsJournal, LiveTeamsJournalCategory, LiveTeamsJournalEntry, LiveTeamsMutation, LiveTeamsMutationResult, LiveTeamsRemoveMemberRequest, LiveTeamsRemoveMemberResult, LiveTeamsRequestContext, LiveTeamsRoleOpenRequest, LiveTeamsRoleSummary, LiveTeamsRoles, LiveTeamsRouteContext, LiveTeamsRouteOptions, LiveTeamsSessionTitleResult, LiveTeamsSnapshot, LiveTeamsSnapshotMember, LiveTeamsTaskDetail, LiveTeamsTaskRouteSummary, LiveTeamsTaskSummary, LiveTeamsTasks, LiveTeamsTeamCandidates, LiveTeamsTitleResult, LiveTeamsWebServer, liveTeamsRequestAllowed, projectLiveTeamsCandidates, projectLiveTeamsSnapshot, registerLiveTeamsRoutes } from "./routes.js";
|
|
10
|
+
import { LiveTeamsService, PluginConfig, apply, inject, name } from "./plugin.js";
|
|
11
|
+
import { CURRENT_SCHEMA_VERSION, atomicWrite, atomicWriteJson, clone, fileExists, readJson, withProcessLock } from "./storage.js";
|
|
12
|
+
import { WorkspaceGuardOptions, assertSessionDescriptorInTeamWorkspace, assertSessionInTeamWorkspace, canonicalPath } from "./binding.js";
|
|
13
|
+
import { MEMBER_TOOL_NAMES, MemberToolsBinding, MemberToolsOptions, MemberToolsResolver, ToolRegistry, registerMemberTools, registerMemberToolsIfAvailable } from "./member-tools.js";
|
|
14
|
+
export { type AcceptTaskRequest, type AcceptTaskResult, type AcknowledgeResult, type AcknowledgeTaskRequest, type AttemptsState, BINDING_STALE_MESSAGE, type BindingGeneration, CURRENT_SCHEMA_VERSION, type ClaimTaskRequest, type ClaimTaskResult, type CommandQueueLike, type CommandRecord, type CommandState, type ContactGrant, type ContactTarget, type CreateMailboxOptions, type CreateTaskRequest, type CreateTaskResult, ERROR_CODES, type ErrorCode, type ErrorOptions, type HumanContact, type JournalCategory, type JournalEntry, type JournalRefs, LIVE_TEAMS_CANDIDATES_ROUTE, LIVE_TEAMS_JOURNAL_ROUTE, LIVE_TEAMS_MEMBERS_ROUTE, LIVE_TEAMS_MEMBER_AVAILABILITY_ROUTE, LIVE_TEAMS_MEMBER_CREATE_SESSION_ROUTE, LIVE_TEAMS_MEMBER_INTERRUPT_ROUTE, LIVE_TEAMS_MEMBER_NAME_ROUTE, LIVE_TEAMS_MEMBER_REMOVE_ROUTE, LIVE_TEAMS_MEMBER_ROLE_ROUTE, LIVE_TEAMS_MESSAGES_ACKNOWLEDGE_ROUTE, LIVE_TEAMS_MESSAGES_ROUTE, LIVE_TEAMS_ROLES_OPEN_ROUTE, LIVE_TEAMS_ROLES_OVERRIDE_ROUTE, LIVE_TEAMS_ROLES_ROUTE, LIVE_TEAMS_TASKS_ROUTE, LIVE_TEAMS_TASK_ACCEPT_ROUTE, LIVE_TEAMS_TASK_REVISE_ROUTE, LIVE_TEAMS_TASK_ROUTE, LIVE_TEAMS_TEAM_ADOPT_ROUTE, LIVE_TEAMS_TEAM_BRIEFING_OPEN_ROUTE, LIVE_TEAMS_TEAM_CANDIDATES_ROUTE, LIVE_TEAMS_TEAM_DISABLE_ROUTE, LIVE_TEAMS_TEAM_ENABLE_ROUTE, LIVE_TEAMS_TEAM_LEAD_ROUTE, LIVE_TEAMS_TEAM_NAME_ROUTE, LIVE_TEAMS_TEAM_ROUTE, type ListTasksOptions, type LiveTeamsAdoptionCandidate, type LiveTeamsAuditRecord, type LiveTeamsCandidate, type LiveTeamsCandidates, type LiveTeamsCreateMemberRequest, type LiveTeamsCreateMemberResult, type LiveTeamsCreateSessionMemberRequest, type LiveTeamsCreateSessionMemberResult, LiveTeamsError, type LiveTeamsJournal, type LiveTeamsJournalCategory, type LiveTeamsJournalEntry, type LiveTeamsMutation, type LiveTeamsMutationResult, type LiveTeamsRemoveMemberRequest, type LiveTeamsRemoveMemberResult, type LiveTeamsRequestContext, type LiveTeamsRole, type LiveTeamsRoleOpenRequest, type LiveTeamsRoleSummary, type LiveTeamsRoles, type LiveTeamsRouteContext, type LiveTeamsRouteOptions, type LiveTeamsService, type LiveTeamsSessionTitleResult, type LiveTeamsSnapshot, type LiveTeamsSnapshotMember, type LiveTeamsTaskDetail, type LiveTeamsTaskRouteSummary, type LiveTeamsTaskSummary, type LiveTeamsTasks, type LiveTeamsTeamCandidates, type LiveTeamsTitleResult, type LiveTeamsWebServer, MEMBERSHIP_CONTEXT_NAME, MEMBERSHIP_CONTEXT_ORDER, MEMBER_TOOL_NAMES, type MailboxActor, type MailboxAuditEvent, type MailboxAuthorizeRequest, type MailboxOptions, type MailboxRecipient, MailboxService, type MemberId, type MemberRecord, type MemberToolsBinding, type MemberToolsOptions, type MemberToolsResolver, type MembershipContextBinding, type MembershipContextOptions, type MembershipContextProvider, type MembershipMember, type MembershipScope, type MessageAckPolicy, type MessageDelivery, type MessageDeliveryOutcome, type MessageDeliveryState, type MessageDraft, type MessageKind, type MessageWakePolicy, type ParsedRole, type PluginConfig, type ReadTaskResult, type ResolvedMailboxRecipient, type ReviewFinding, type ReviewRound, type ReviewTaskRequest, type ReviewTaskResult, type ReviewVerdict, type ReviewsState, type ReviseTaskRequest, type ReviseTaskResult, type RolePermissions, type RoleRegistry, type RoleRegistrySync, type SendResult, type SessionBridge, type SessionBridgeDeps, SessionCommandQueue, type SessionController, type SessionDescriptor, type SessionId, type Submission, type SubmissionArtifact, type SubmissionCheck, type SubmissionSourceArtifact, type SubmissionsState, type SubmitTaskRequest, TEAM_ROOT, type TaskAttempt, type TaskKind, type TaskRoute, type TaskStatus, TaskStore, type TasksState, type TeamAdoption, type TeamCandidate, type TeamId, type TeamMessage, type TeamState, TeamStore, type TeamTask, type ToolRegistry, type WorkspaceGuardOptions, apply, assertSessionDescriptorInTeamWorkspace, assertSessionInTeamWorkspace, assignmentRouteText, atomicWrite, atomicWriteJson, canonicalPath, clone, createCommandQueue, createMailbox, createMembershipContextProvider, createSessionBridge, createTaskStore, createTeamStore, apply as default, deriveRouteContacts, fileExists, inject, liveTeamsRequestAllowed, messageDeliveryOutcome, messagePolicy, name, parseRoleDocument, platformErrorMappings, projectLiveTeamsCandidates, projectLiveTeamsSnapshot, readJson, readMemberBySessionSync, readRoleRegistry, readRoleRegistrySync, readTeamStateSync, registerLiveTeamsRoutes, registerMemberTools, registerMemberToolsIfAvailable, renderDocument, renderMembership, resolveMailboxRecipient, routeAllowsContact, routeText, sessionIdOf, taskDocumentPath, toBridgeError, translatePlatformError, withProcessLock };
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BINDING_STALE_MESSAGE, ERROR_CODES, LiveTeamsError, platformErrorMappings, translatePlatformError } from "./errors.js";
|
|
2
|
+
import { CURRENT_SCHEMA_VERSION, atomicWrite, atomicWriteJson, clone, fileExists, readJson, withProcessLock } from "./storage.js";
|
|
3
|
+
import { TEAM_ROOT, TeamStore, createTeamStore, readMemberBySessionSync, readTeamStateSync } from "./team-store.js";
|
|
4
|
+
import { parseRoleDocument, readRoleRegistry, readRoleRegistrySync } from "./roles.js";
|
|
5
|
+
import { createSessionBridge, toBridgeError } from "./session-bridge.js";
|
|
6
|
+
import { SessionCommandQueue, createCommandQueue } from "./command-queue.js";
|
|
7
|
+
import { MailboxService, createMailbox, messageDeliveryOutcome, messagePolicy, resolveMailboxRecipient } from "./mailbox.js";
|
|
8
|
+
import { TaskStore, assignmentRouteText, createTaskStore, deriveRouteContacts, renderDocument, routeAllowsContact, routeText, taskDocumentPath } from "./task-store.js";
|
|
9
|
+
import { MEMBER_TOOL_NAMES, registerMemberTools, registerMemberToolsIfAvailable } from "./member-tools.js";
|
|
10
|
+
import { MEMBERSHIP_CONTEXT_NAME, MEMBERSHIP_CONTEXT_ORDER, createMembershipContextProvider, renderMembership, sessionIdOf } from "./context-provider.js";
|
|
11
|
+
import { assertSessionDescriptorInTeamWorkspace, assertSessionInTeamWorkspace, canonicalPath } from "./binding.js";
|
|
12
|
+
import { LIVE_TEAMS_CANDIDATES_ROUTE, LIVE_TEAMS_JOURNAL_ROUTE, LIVE_TEAMS_MEMBERS_ROUTE, LIVE_TEAMS_MEMBER_AVAILABILITY_ROUTE, LIVE_TEAMS_MEMBER_CREATE_SESSION_ROUTE, LIVE_TEAMS_MEMBER_INTERRUPT_ROUTE, LIVE_TEAMS_MEMBER_NAME_ROUTE, LIVE_TEAMS_MEMBER_REMOVE_ROUTE, LIVE_TEAMS_MEMBER_ROLE_ROUTE, LIVE_TEAMS_MESSAGES_ACKNOWLEDGE_ROUTE, LIVE_TEAMS_MESSAGES_ROUTE, LIVE_TEAMS_ROLES_OPEN_ROUTE, LIVE_TEAMS_ROLES_OVERRIDE_ROUTE, LIVE_TEAMS_ROLES_ROUTE, LIVE_TEAMS_TASKS_ROUTE, LIVE_TEAMS_TASK_ACCEPT_ROUTE, LIVE_TEAMS_TASK_REVISE_ROUTE, LIVE_TEAMS_TASK_ROUTE, LIVE_TEAMS_TEAM_ADOPT_ROUTE, LIVE_TEAMS_TEAM_BRIEFING_OPEN_ROUTE, LIVE_TEAMS_TEAM_CANDIDATES_ROUTE, LIVE_TEAMS_TEAM_DISABLE_ROUTE, LIVE_TEAMS_TEAM_ENABLE_ROUTE, LIVE_TEAMS_TEAM_LEAD_ROUTE, LIVE_TEAMS_TEAM_NAME_ROUTE, LIVE_TEAMS_TEAM_ROUTE, liveTeamsRequestAllowed, projectLiveTeamsCandidates, projectLiveTeamsSnapshot, registerLiveTeamsRoutes } from "./routes.js";
|
|
13
|
+
import { apply, inject, name } from "./plugin.js";
|
|
14
|
+
export { BINDING_STALE_MESSAGE, CURRENT_SCHEMA_VERSION, ERROR_CODES, LIVE_TEAMS_CANDIDATES_ROUTE, LIVE_TEAMS_JOURNAL_ROUTE, LIVE_TEAMS_MEMBERS_ROUTE, LIVE_TEAMS_MEMBER_AVAILABILITY_ROUTE, LIVE_TEAMS_MEMBER_CREATE_SESSION_ROUTE, LIVE_TEAMS_MEMBER_INTERRUPT_ROUTE, LIVE_TEAMS_MEMBER_NAME_ROUTE, LIVE_TEAMS_MEMBER_REMOVE_ROUTE, LIVE_TEAMS_MEMBER_ROLE_ROUTE, LIVE_TEAMS_MESSAGES_ACKNOWLEDGE_ROUTE, LIVE_TEAMS_MESSAGES_ROUTE, LIVE_TEAMS_ROLES_OPEN_ROUTE, LIVE_TEAMS_ROLES_OVERRIDE_ROUTE, LIVE_TEAMS_ROLES_ROUTE, LIVE_TEAMS_TASKS_ROUTE, LIVE_TEAMS_TASK_ACCEPT_ROUTE, LIVE_TEAMS_TASK_REVISE_ROUTE, LIVE_TEAMS_TASK_ROUTE, LIVE_TEAMS_TEAM_ADOPT_ROUTE, LIVE_TEAMS_TEAM_BRIEFING_OPEN_ROUTE, LIVE_TEAMS_TEAM_CANDIDATES_ROUTE, LIVE_TEAMS_TEAM_DISABLE_ROUTE, LIVE_TEAMS_TEAM_ENABLE_ROUTE, LIVE_TEAMS_TEAM_LEAD_ROUTE, LIVE_TEAMS_TEAM_NAME_ROUTE, LIVE_TEAMS_TEAM_ROUTE, LiveTeamsError, MEMBERSHIP_CONTEXT_NAME, MEMBERSHIP_CONTEXT_ORDER, MEMBER_TOOL_NAMES, MailboxService, SessionCommandQueue, TEAM_ROOT, TaskStore, TeamStore, apply, assertSessionDescriptorInTeamWorkspace, assertSessionInTeamWorkspace, assignmentRouteText, atomicWrite, atomicWriteJson, canonicalPath, clone, createCommandQueue, createMailbox, createMembershipContextProvider, createSessionBridge, createTaskStore, createTeamStore, apply as default, deriveRouteContacts, fileExists, inject, liveTeamsRequestAllowed, messageDeliveryOutcome, messagePolicy, name, parseRoleDocument, platformErrorMappings, projectLiveTeamsCandidates, projectLiveTeamsSnapshot, readJson, readMemberBySessionSync, readRoleRegistry, readRoleRegistrySync, readTeamStateSync, registerLiveTeamsRoutes, registerMemberTools, registerMemberToolsIfAvailable, renderDocument, renderMembership, resolveMailboxRecipient, routeAllowsContact, routeText, sessionIdOf, taskDocumentPath, toBridgeError, translatePlatformError, withProcessLock };
|